user_driver/
lib.rs

1// Copyright (c) Microsoft Corporation.
2// Licensed under the MIT License.
3
4//! Infrastructure for implementing PCI drivers in user mode.
5
6// UNSAFETY: Manual memory management around buffers and mmap.
7#![expect(unsafe_code)]
8#![expect(missing_docs)]
9
10use inspect::Inspect;
11use interrupt::DeviceInterrupt;
12use memory::MemoryBlock;
13use std::sync::Arc;
14
15pub mod backoff;
16pub mod interrupt;
17pub mod lockmem;
18pub mod memory;
19pub mod page_allocator;
20pub mod vfio;
21
22pub enum DmaPool {
23    Ephemeral,
24    Persistent,
25}
26
27/// An interface to access device hardware.
28pub trait DeviceBacking: 'static + Send + Inspect {
29    /// An object for accessing device registers.
30    type Registers: 'static + DeviceRegisterIo + Inspect;
31
32    /// Returns a device ID for diagnostics.
33    fn id(&self) -> &str;
34
35    /// Maps a BAR.
36    fn map_bar(&mut self, n: u8) -> anyhow::Result<Self::Registers>;
37
38    /// DMA Client for the device.
39    fn dma_client(&self) -> Arc<dyn DmaClient>;
40
41    /// Overloaded DMA Client for the device, based on the requested pool.
42    ///
43    /// Default implementation returns an error as this is only currently implemented
44    /// by VfioDevice's implementation.
45    fn dma_client_for(&self, _pool: DmaPool) -> anyhow::Result<Arc<dyn DmaClient>> {
46        anyhow::bail!("multiple dma clients are not supported by this DmaClient");
47    }
48
49    /// Returns the maximum number of interrupts that can be mapped.
50    fn max_interrupt_count(&self) -> u32;
51
52    /// Maps a MSI-X interrupt for use, returning an object that can be used to
53    /// wait for the interrupt to be signaled by the device.
54    ///
55    /// `cpu` is the CPU that the device should target with this interrupt.
56    ///
57    /// This can be called multiple times for the same interrupt without disconnecting
58    /// previous mappings. The last `cpu` value will be used as the target CPU.
59    fn map_interrupt(&mut self, msix: u32, cpu: u32) -> anyhow::Result<DeviceInterrupt>;
60
61    /// Unmaps and disables all previously mapped interrupts.
62    ///
63    /// Default implementation is a no-op for backends that do not support interrupt unmapping.
64    fn unmap_all_interrupts(&mut self) -> anyhow::Result<()> {
65        Ok(())
66    }
67}
68
69/// Access to device registers.
70pub trait DeviceRegisterIo: Send + Sync {
71    /// Returns the length of the register space.
72    fn len(&self) -> usize;
73    /// Reads a `u32` register.
74    fn read_u32(&self, offset: usize) -> u32;
75    /// Reads a `u64` register.
76    fn read_u64(&self, offset: usize) -> u64;
77    /// Writes a `u32` register.
78    fn write_u32(&self, offset: usize, data: u32);
79    /// Writes a `u64` register.
80    fn write_u64(&self, offset: usize, data: u64);
81}
82
83/// Device interfaces for DMA.
84pub trait DmaClient: Send + Sync + Inspect {
85    /// Allocate a new DMA buffer. This buffer must be zero initialized.
86    ///
87    /// TODO: string tag for allocation?
88    /// TODO: contiguous vs non-contiguous? (both on the request side, and if
89    ///       a request contiguous allocation cannot be fulfilled)
90    fn allocate_dma_buffer(&self, total_size: usize) -> anyhow::Result<MemoryBlock>;
91
92    /// Attach all previously allocated memory blocks.
93    fn attach_pending_buffers(&self) -> anyhow::Result<Vec<MemoryBlock>>;
94}