Skip to content

Commit 1cbd2a5

Browse files
committed
iommufd-ioctls: Add ioctl wrappers for vIOMMU/vDevice operations
Extend the iommufd ioctl wrapper library to enable userspace VMMs to build virtual IOMMU that can leverage hardware IOMMU acceleration. Added ioctl wrappers: - IOMMU_DESTROY: Generic destruction of iommufd objects - IOMMU_HWPT_ALLOC: Allocate hardware page tables for nested translation - IOMMUFD_GET_HW_INFO: Query physical IOMMU hardware capabilities - IOMMUFD_HWPT_INVALIDATE: Invalidate cached page table entries - IOMMU_VIOMMU_ALLOC: Allocate virtual IOMMU instances backed by hardware - IOMMU_VDEVICE_ALLOC: Allocate virtual devices under a vIOMMU By exposing these hardware-accelerated operations to userspace, VMMs can implement nested IOMMU virtualization where the physical IOMMU hardware directly processes guest page tables, eliminating expensive emulation and VM exits for IOMMU operations. Signed-off-by: Bo Chen <bchen@crusoe.ai>
1 parent 670b638 commit 1cbd2a5

File tree

2 files changed

+170
-0
lines changed

2 files changed

+170
-0
lines changed

iommufd-ioctls/src/iommufd_ioctls.rs

Lines changed: 158 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -26,16 +26,45 @@ impl IommuFd {
2626
Ok(IommuFd { iommufd })
2727
}
2828

29+
pub fn destroy_iommufd(&self, id: u32) -> Result<()> {
30+
let destroy_data = iommu_destroy {
31+
size: std::mem::size_of::<iommu_destroy>() as u32,
32+
id,
33+
};
34+
iommufd_syscall::destroy_iommufd(self, &destroy_data)
35+
}
36+
2937
pub fn alloc_iommu_ioas(&self, alloc_data: &mut iommu_ioas_alloc) -> Result<()> {
3038
iommufd_syscall::alloc_iommu_ioas(self, alloc_data)
3139
}
3240

3341
pub fn map_iommu_ioas(&self, map: &iommu_ioas_map) -> Result<()> {
3442
iommufd_syscall::map_iommu_ioas(self, map)
3543
}
44+
3645
pub fn unmap_iommu_ioas(&self, unmap: &mut iommu_ioas_unmap) -> Result<()> {
3746
iommufd_syscall::unmap_iommu_ioas(self, unmap)
3847
}
48+
49+
pub fn alloc_iommu_hwpt(&self, hwpt_alloc: &mut iommu_hwpt_alloc) -> Result<()> {
50+
iommufd_syscall::alloc_iommu_hwpt(self, hwpt_alloc)
51+
}
52+
53+
pub fn get_hw_info(&self, hw_info: &mut iommu_hw_info) -> Result<()> {
54+
iommufd_syscall::get_hw_info(self, hw_info)
55+
}
56+
57+
pub fn invalidate_hwpt(&self, hwpt_invalidate: &mut iommu_hwpt_invalidate) -> Result<()> {
58+
iommufd_syscall::invalidate_hwpt(self, hwpt_invalidate)
59+
}
60+
61+
pub fn alloc_iommu_viommu(&self, viommu_alloc: &mut iommu_viommu_alloc) -> Result<()> {
62+
iommufd_syscall::alloc_iommu_viommu(self, viommu_alloc)
63+
}
64+
65+
pub fn alloc_iommu_vdevice(&self, vdevice_alloc: &mut iommu_vdevice_alloc) -> Result<()> {
66+
iommufd_syscall::alloc_iommu_vdevice(self, vdevice_alloc)
67+
}
3968
}
4069

4170
impl AsRawFd for IommuFd {
@@ -44,6 +73,7 @@ impl AsRawFd for IommuFd {
4473
}
4574
}
4675

76+
ioctl_io_nr!(IOMMU_DESTROY, IOMMUFD_TYPE as u32, IOMMUFD_CMD_DESTROY);
4777
ioctl_io_nr!(
4878
IOMMU_IOAS_ALLOC,
4979
IOMMUFD_TYPE as u32,
@@ -55,6 +85,31 @@ ioctl_io_nr!(
5585
IOMMUFD_TYPE as u32,
5686
IOMMUFD_CMD_IOAS_UNMAP
5787
);
88+
ioctl_io_nr!(
89+
IOMMU_HWPT_ALLOC,
90+
IOMMUFD_TYPE as u32,
91+
IOMMUFD_CMD_HWPT_ALLOC
92+
);
93+
ioctl_io_nr!(
94+
IOMMUFD_GET_HW_INFO,
95+
IOMMUFD_TYPE as u32,
96+
IOMMUFD_CMD_GET_HW_INFO
97+
);
98+
ioctl_io_nr!(
99+
IOMMUFD_HWPT_INVALIDATE,
100+
IOMMUFD_TYPE as u32,
101+
IOMMUFD_CMD_HWPT_INVALIDATE
102+
);
103+
ioctl_io_nr!(
104+
IOMMU_VIOMMU_ALLOC,
105+
IOMMUFD_TYPE as u32,
106+
IOMMUFD_CMD_VIOMMU_ALLOC
107+
);
108+
ioctl_io_nr!(
109+
IOMMU_VDEVICE_ALLOC,
110+
IOMMUFD_TYPE as u32,
111+
IOMMUFD_CMD_VDEVICE_ALLOC
112+
);
58113

59114
// Safety:
60115
// - absolutely trust the underlying kernel
@@ -64,6 +119,19 @@ pub(crate) mod iommufd_syscall {
64119
use super::*;
65120
use vmm_sys_util::ioctl::{ioctl_with_mut_ref, ioctl_with_ref};
66121

122+
pub(crate) fn destroy_iommufd(iommufd: &IommuFd, destroy_data: &iommu_destroy) -> Result<()> {
123+
// SAFETY:
124+
// 1. The file descriptor provided by 'iommufd' is valid and open.
125+
// 2. The 'destroy_data' points to initialized memory with expected data structure,
126+
// and remains valid for the duration of sysca
127+
// 3. The return value is checked.
128+
let ret = unsafe { ioctl_with_ref(iommufd, IOMMU_DESTROY(), destroy_data) };
129+
if ret < 0 {
130+
Err(IommufdError::IommuDestroy(SysError::last()))
131+
} else {
132+
Ok(())
133+
}
134+
}
67135
pub(crate) fn alloc_iommu_ioas(
68136
iommufd: &IommuFd,
69137
alloc_data: &mut iommu_ioas_alloc,
@@ -94,6 +162,7 @@ pub(crate) mod iommufd_syscall {
94162
Ok(())
95163
}
96164
}
165+
97166
pub(crate) fn unmap_iommu_ioas(iommufd: &IommuFd, unmap: &mut iommu_ioas_unmap) -> Result<()> {
98167
// SAFETY:
99168
// 1. The file descriptor provided by 'iommufd' is valid and open.
@@ -107,6 +176,89 @@ pub(crate) mod iommufd_syscall {
107176
Ok(())
108177
}
109178
}
179+
180+
pub(crate) fn alloc_iommu_hwpt(
181+
iommufd: &IommuFd,
182+
hwpt_alloc: &mut iommu_hwpt_alloc,
183+
) -> Result<()> {
184+
// SAFETY:
185+
// 1. The file descriptor provided by 'iommufd' is valid and open.
186+
// 2. The 'hwpt_alloc' points to initialized memory with expected data structure,
187+
// and remains valid for the duration of syscall.
188+
// 3. The return value is checked.
189+
let ret = unsafe { ioctl_with_mut_ref(iommufd, IOMMU_HWPT_ALLOC(), hwpt_alloc) };
190+
if ret < 0 {
191+
Err(IommufdError::IommuHwptAlloc(SysError::last()))
192+
} else {
193+
Ok(())
194+
}
195+
}
196+
197+
pub(crate) fn get_hw_info(iommufd: &IommuFd, hw_info: &mut iommu_hw_info) -> Result<()> {
198+
// SAFETY:
199+
// 1. The file descriptor provided by 'iommufd' is valid and open.
200+
// 2. The 'hw_info' points to initialized memory with expected data structure,
201+
// and remains valid for the duration of syscall.
202+
// 3. The return value is checked.
203+
let ret = unsafe { ioctl_with_mut_ref(iommufd, IOMMUFD_GET_HW_INFO(), hw_info) };
204+
if ret < 0 {
205+
Err(IommufdError::IommuGetHwInfo(SysError::last()))
206+
} else {
207+
Ok(())
208+
}
209+
}
210+
211+
pub(crate) fn invalidate_hwpt(
212+
iommufd: &IommuFd,
213+
hwpt_invalidate: &mut iommu_hwpt_invalidate,
214+
) -> Result<()> {
215+
// SAFETY:
216+
// 1. The file descriptor provided by 'iommufd' is valid and open.
217+
// 2. The 'hwpt_invalidate' points to initialized memory with expected data structure,
218+
// and remains valid for the duration of syscall.
219+
// 3. The return value is checked.
220+
let ret =
221+
unsafe { ioctl_with_mut_ref(iommufd, IOMMUFD_HWPT_INVALIDATE(), hwpt_invalidate) };
222+
if ret < 0 {
223+
Err(IommufdError::IommuHwptInvalidate(SysError::last()))
224+
} else {
225+
Ok(())
226+
}
227+
}
228+
229+
pub(crate) fn alloc_iommu_viommu(
230+
iommufd: &IommuFd,
231+
viommu_alloc: &mut iommu_viommu_alloc,
232+
) -> Result<()> {
233+
// SAFETY:
234+
// 1. The file descriptor provided by 'iommufd' is valid and open.
235+
// 2. The 'viommu_alloc' points to initialized memory with expected data structure,
236+
// and remains valid for the duration of syscall.
237+
// 3. The return value is checked.
238+
let ret = unsafe { ioctl_with_mut_ref(iommufd, IOMMU_VIOMMU_ALLOC(), viommu_alloc) };
239+
if ret < 0 {
240+
Err(IommufdError::IommuViommuAlloc(SysError::last()))
241+
} else {
242+
Ok(())
243+
}
244+
}
245+
246+
pub(crate) fn alloc_iommu_vdevice(
247+
iommufd: &IommuFd,
248+
vdevice_alloc: &mut iommu_vdevice_alloc,
249+
) -> Result<()> {
250+
// SAFETY:
251+
// 1. The file descriptor provided by 'iommufd' is valid and open.
252+
// 2. The 'vdevice_alloc' points to initialized memory with expected data structure,
253+
// and remains valid for the duration of syscall.
254+
// 3. The return value is checked.
255+
let ret = unsafe { ioctl_with_mut_ref(iommufd, IOMMU_VDEVICE_ALLOC(), vdevice_alloc) };
256+
if ret < 0 {
257+
Err(IommufdError::IommuVdeviceAlloc(SysError::last()))
258+
} else {
259+
Ok(())
260+
}
261+
}
110262
}
111263

112264
#[cfg(test)]
@@ -115,8 +267,14 @@ mod tests {
115267

116268
#[test]
117269
fn test_iommufd_ioctl_code() {
270+
assert_eq!(IOMMU_DESTROY(), 15232);
118271
assert_eq!(IOMMU_IOAS_ALLOC(), 15233);
119272
assert_eq!(IOMMU_IOAS_MAP(), 15237);
120273
assert_eq!(IOMMU_IOAS_UNMAP(), 15238);
274+
assert_eq!(IOMMU_HWPT_ALLOC(), 15241);
275+
assert_eq!(IOMMUFD_GET_HW_INFO(), 15242);
276+
assert_eq!(IOMMUFD_HWPT_INVALIDATE(), 15245);
277+
assert_eq!(IOMMU_VIOMMU_ALLOC(), 15248);
278+
assert_eq!(IOMMU_VDEVICE_ALLOC(), 15249);
121279
}
122280
}

iommufd-ioctls/src/lib.rs

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -18,12 +18,24 @@ pub use iommufd_ioctls::*;
1818
pub enum IommufdError {
1919
#[error("failed to open /dev/iommufd: {0}")]
2020
OpenIommufd(#[source] io::Error),
21+
#[error("failed to destroy iommufd: {0}")]
22+
IommuDestroy(#[source] SysError),
2123
#[error("failed to allocate IOAS: {0}")]
2224
IommuIoasAlloc(#[source] SysError),
2325
#[error("failed to map an IOVA range to the IOAS: {0}")]
2426
IommuIoasMap(#[source] SysError),
2527
#[error("failed to unmap an IOVA range from the IOAS: {0}")]
2628
IommuIoasUnmap(#[source] SysError),
29+
#[error("failed to allocate HWPT: {0}")]
30+
IommuHwptAlloc(#[source] SysError),
31+
#[error("failed to allocate vIOMMU: {0}")]
32+
IommuViommuAlloc(#[source] SysError),
33+
#[error("failed to allocate vDevice: {0}")]
34+
IommuVdeviceAlloc(#[source] SysError),
35+
#[error("failed to get HW info: {0}")]
36+
IommuGetHwInfo(#[source] SysError),
37+
#[error("failed to invalidate HWPT: {0}")]
38+
IommuHwptInvalidate(#[source] SysError),
2739
}
2840

2941
pub type Result<T> = std::result::Result<T, IommufdError>;

0 commit comments

Comments
 (0)