pci_driver/
iommu.rs

1// SPDX-License-Identifier: MIT OR Apache-2.0
2
3/* ---------------------------------------------------------------------------------------------- */
4
5use std::io;
6use std::ops::Range;
7
8use crate::regions::Permissions;
9
10/* ---------------------------------------------------------------------------------------------- */
11
12/// Represents an IOMMU that controls DMA done by some PCI function, device, or group of devices.
13///
14/// You'll probably need [`std::sync::atomic::fence`] or use types like
15/// [`AtomicU32`](std::sync::atomic::AtomicU32) somewhere to synchronize accesses properly with the
16/// device.
17pub struct PciIommu<'a> {
18    pub(crate) internal: &'a dyn PciIommuInternal,
19}
20
21impl PciIommu<'_> {
22    /// Both `iova` and process `address` must be aligned to this value.
23    ///
24    /// This is always a power of 2, and never less than the system's page size.
25    pub fn alignment(&self) -> usize {
26        self.internal.alignment()
27    }
28
29    /// IOVA ranges given to [`PciIommu::map`] must be contained in one of the ranges that this
30    /// method returns.
31    pub fn valid_iova_ranges(&self) -> &[Range<u64>] {
32        self.internal.valid_iova_ranges()
33    }
34
35    /// The maximum number of mappings that may be in effect simultaneously.
36    pub fn max_num_mappings(&self) -> u32 {
37        self.internal.max_num_mappings()
38    }
39
40    /// Add the given mapping to the IOMMU.
41    ///
42    /// - `iova` is the start address of the region in the device's address space.
43    /// - `size` is the length of the region.
44    /// - `address` is a pointer (in the current process' address space) to the start of the region
45    ///   to be mapped.
46    ///
47    /// TODO: Alignment constraints?
48    ///
49    /// # Safety
50    ///
51    /// Must make sense.
52    pub unsafe fn map(
53        &self,
54        iova: u64,
55        length: usize,
56        address: *const u8,
57        device_permissions: Permissions,
58    ) -> io::Result<()> {
59        unsafe { self.internal.map(iova, length, address, device_permissions) }
60    }
61
62    /// Remove the given mapping from the IOMMU.
63    ///
64    /// TODO: Alignment constraints?
65    ///
66    /// Must unmap exactly a full range that was previously mapped using [`PciIommu::map`], or
67    /// several full ranges as long as they are contiguous. Otherwise, this fails.
68    pub fn unmap(&self, iova: u64, size: usize) -> io::Result<()> {
69        self.internal.unmap(iova, size)
70    }
71}
72
73/* ---------------------------------------------------------------------------------------------- */
74
75pub(crate) trait PciIommuInternal {
76    fn alignment(&self) -> usize;
77
78    fn valid_iova_ranges(&self) -> &[Range<u64>];
79
80    fn max_num_mappings(&self) -> u32;
81
82    unsafe fn map(
83        &self,
84        iova: u64,
85        length: usize,
86        address: *const u8,
87        device_permissions: Permissions,
88    ) -> io::Result<()>;
89
90    fn unmap(&self, iova: u64, length: usize) -> io::Result<()>;
91}
92
93/* ---------------------------------------------------------------------------------------------- */