Skip to content

Commit 870fe23

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 17790e0 commit 870fe23

File tree

2 files changed

+171
-0
lines changed

2 files changed

+171
-0
lines changed

iommufd-ioctls/src/iommufd_ioctls.rs

Lines changed: 159 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -26,16 +26,46 @@ 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+
..Default::default()
34+
};
35+
iommufd_syscall::destroy_iommufd(self, &destroy_data)
36+
}
37+
2938
pub fn alloc_iommu_ioas(&self, alloc_data: &mut iommu_ioas_alloc) -> Result<()> {
3039
iommufd_syscall::alloc_iommu_ioas(self, alloc_data)
3140
}
3241

3342
pub fn map_iommu_ioas(&self, map: &iommu_ioas_map) -> Result<()> {
3443
iommufd_syscall::map_iommu_ioas(self, map)
3544
}
45+
3646
pub fn unmap_iommu_ioas(&self, unmap: &mut iommu_ioas_unmap) -> Result<()> {
3747
iommufd_syscall::unmap_iommu_ioas(self, unmap)
3848
}
49+
50+
pub fn alloc_iommu_hwpt(&self, hwpt_alloc: &mut iommu_hwpt_alloc) -> Result<()> {
51+
iommufd_syscall::alloc_iommu_hwpt(self, hwpt_alloc)
52+
}
53+
54+
pub fn get_hw_info(&self, hw_info: &mut iommu_hw_info) -> Result<()> {
55+
iommufd_syscall::get_hw_info(self, hw_info)
56+
}
57+
58+
pub fn invalidate_hwpt(&self, hwpt_invalidate: &mut iommu_hwpt_invalidate) -> Result<()> {
59+
iommufd_syscall::invalidate_hwpt(self, hwpt_invalidate)
60+
}
61+
62+
pub fn alloc_iommu_viommu(&self, viommu_alloc: &mut iommu_viommu_alloc) -> Result<()> {
63+
iommufd_syscall::alloc_iommu_viommu(self, viommu_alloc)
64+
}
65+
66+
pub fn alloc_iommu_vdevice(&self, vdevice_alloc: &mut iommu_vdevice_alloc) -> Result<()> {
67+
iommufd_syscall::alloc_iommu_vdevice(self, vdevice_alloc)
68+
}
3969
}
4070

4171
impl AsRawFd for IommuFd {
@@ -44,6 +74,7 @@ impl AsRawFd for IommuFd {
4474
}
4575
}
4676

77+
ioctl_io_nr!(IOMMU_DESTROY, IOMMUFD_TYPE as u32, IOMMUFD_CMD_DESTROY);
4778
ioctl_io_nr!(
4879
IOMMU_IOAS_ALLOC,
4980
IOMMUFD_TYPE as u32,
@@ -55,6 +86,31 @@ ioctl_io_nr!(
5586
IOMMUFD_TYPE as u32,
5687
IOMMUFD_CMD_IOAS_UNMAP
5788
);
89+
ioctl_io_nr!(
90+
IOMMU_HWPT_ALLOC,
91+
IOMMUFD_TYPE as u32,
92+
IOMMUFD_CMD_HWPT_ALLOC
93+
);
94+
ioctl_io_nr!(
95+
IOMMUFD_GET_HW_INFO,
96+
IOMMUFD_TYPE as u32,
97+
IOMMUFD_CMD_GET_HW_INFO
98+
);
99+
ioctl_io_nr!(
100+
IOMMUFD_HWPT_INVALIDATE,
101+
IOMMUFD_TYPE as u32,
102+
IOMMUFD_CMD_HWPT_INVALIDATE
103+
);
104+
ioctl_io_nr!(
105+
IOMMU_VIOMMU_ALLOC,
106+
IOMMUFD_TYPE as u32,
107+
IOMMUFD_CMD_VIOMMU_ALLOC
108+
);
109+
ioctl_io_nr!(
110+
IOMMU_VDEVICE_ALLOC,
111+
IOMMUFD_TYPE as u32,
112+
IOMMUFD_CMD_VDEVICE_ALLOC
113+
);
58114

59115
// Safety:
60116
// - absolutely trust the underlying kernel
@@ -64,6 +120,19 @@ pub(crate) mod iommufd_syscall {
64120
use super::*;
65121
use vmm_sys_util::ioctl::{ioctl_with_mut_ref, ioctl_with_ref};
66122

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

112265
#[cfg(test)]
@@ -115,8 +268,14 @@ mod tests {
115268

116269
#[test]
117270
fn test_iommufd_ioctl_code() {
271+
assert_eq!(IOMMU_DESTROY(), 15232);
118272
assert_eq!(IOMMU_IOAS_ALLOC(), 15233);
119273
assert_eq!(IOMMU_IOAS_MAP(), 15237);
120274
assert_eq!(IOMMU_IOAS_UNMAP(), 15238);
275+
assert_eq!(IOMMU_HWPT_ALLOC(), 15241);
276+
assert_eq!(IOMMUFD_GET_HW_INFO(), 15242);
277+
assert_eq!(IOMMUFD_HWPT_INVALIDATE(), 15245);
278+
assert_eq!(IOMMU_VIOMMU_ALLOC(), 15248);
279+
assert_eq!(IOMMU_VDEVICE_ALLOC(), 15249);
121280
}
122281
}

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)