vm_memory/udmabuf/sys/
linux.rs1#![allow(dead_code)]
6
7use std::fs::File;
8use std::fs::OpenOptions;
9use std::io::Error as IoError;
10use std::os::raw::c_uint;
11use std::path::Path;
12
13use base::ioctl_iow_nr;
14use base::ioctl_with_ref;
15use base::pagesize;
16use base::FromRawDescriptor;
17use base::MappedRegion;
18use base::SafeDescriptor;
19use zerocopy::FromZeros;
20
21use crate::udmabuf::UdmabufDriverTrait;
22use crate::udmabuf::UdmabufError;
23use crate::udmabuf::UdmabufResult;
24use crate::udmabuf_bindings::*;
25use crate::GuestAddress;
26use crate::GuestMemory;
27use crate::GuestMemoryError;
28
29const UDMABUF_IOCTL_BASE: c_uint = 0x75;
30
31ioctl_iow_nr!(UDMABUF_CREATE, UDMABUF_IOCTL_BASE, 0x42, udmabuf_create);
32ioctl_iow_nr!(
33 UDMABUF_CREATE_LIST,
34 UDMABUF_IOCTL_BASE,
35 0x43,
36 udmabuf_create_list
37);
38
39fn memory_offset(mem: &GuestMemory, guest_addr: GuestAddress, len: u64) -> UdmabufResult<u64> {
51 let (mapping, map_offset, memfd_offset) = mem
52 .find_region(guest_addr)
53 .map_err(UdmabufError::InvalidOffset)?;
54 let map_offset = map_offset as u64;
55 if map_offset
56 .checked_add(len)
57 .is_none_or(|a| a > mapping.size() as u64)
58 {
59 return Err(UdmabufError::InvalidOffset(
60 GuestMemoryError::InvalidGuestAddress(guest_addr),
61 ));
62 }
63
64 Ok(memfd_offset + map_offset)
65}
66
67pub struct UnixUdmabufDriver {
73 driver_fd: File,
74}
75
76impl UdmabufDriverTrait for UnixUdmabufDriver {
77 fn new() -> UdmabufResult<UnixUdmabufDriver> {
78 const UDMABUF_PATH: &str = "/dev/udmabuf";
79 let path = Path::new(UDMABUF_PATH);
80 let fd = OpenOptions::new()
81 .read(true)
82 .write(true)
83 .open(path)
84 .map_err(UdmabufError::DriverOpenFailed)?;
85
86 Ok(UnixUdmabufDriver { driver_fd: fd })
87 }
88
89 fn create_udmabuf(
90 &self,
91 mem: &GuestMemory,
92 iovecs: &[(GuestAddress, usize)],
93 ) -> UdmabufResult<SafeDescriptor> {
94 let pgsize = pagesize();
95
96 let mut list =
97 udmabuf_create_list::<[udmabuf_create_item]>::new_box_zeroed_with_elems(iovecs.len())
98 .unwrap();
99 list.flags = UDMABUF_FLAGS_CLOEXEC;
100 list.count = iovecs.len().try_into().unwrap();
101 for (i, &(addr, len)) in iovecs.iter().enumerate() {
102 let offset = memory_offset(mem, addr, len as u64)?;
103
104 if offset as usize % pgsize != 0 || len % pgsize != 0 {
105 return Err(UdmabufError::NotPageAligned);
106 }
107
108 list.list[i].memfd = mem.shm_region(addr).unwrap().as_raw_descriptor() as u32;
110 list.list[i].__pad = 0;
111 list.list[i].offset = offset;
112 list.list[i].size = len as u64;
113 }
114
115 let fd = unsafe { ioctl_with_ref(&self.driver_fd, UDMABUF_CREATE_LIST, &*list) };
118
119 if fd < 0 {
120 return Err(UdmabufError::DmabufCreationFail(IoError::last_os_error()));
121 }
122
123 Ok(unsafe { SafeDescriptor::from_raw_descriptor(fd) })
126 }
127}
128
129#[cfg(test)]
130mod tests {
131 use super::*;
132 use crate::GuestAddress;
133
134 #[test]
135 fn test_memory_offsets() {
136 let start_addr1 = GuestAddress(0x100);
137 let start_addr2 = GuestAddress(0x100 + pagesize() as u64);
138 let start_addr3 = GuestAddress(0x100 + 2 * pagesize() as u64);
139
140 let mem = GuestMemory::new(&[
141 (start_addr1, pagesize() as u64),
142 (start_addr2, pagesize() as u64),
143 (start_addr3, pagesize() as u64),
144 ])
145 .unwrap();
146
147 assert_eq!(memory_offset(&mem, GuestAddress(0x300), 1).unwrap(), 0x200);
148 assert_eq!(
149 memory_offset(&mem, GuestAddress(0x200 + pagesize() as u64), 1).unwrap(),
150 0x100 + pagesize() as u64,
151 );
152 assert_eq!(
153 memory_offset(
154 &mem,
155 GuestAddress(0x100 + pagesize() as u64),
156 pagesize() as u64
157 )
158 .unwrap(),
159 pagesize() as u64,
160 );
161 assert!(memory_offset(
162 &mem,
163 GuestAddress(0x100 + pagesize() as u64),
164 1 + pagesize() as u64
165 )
166 .is_err());
167 }
168
169 #[test]
170 fn test_udmabuf_create() {
171 let driver_result = UnixUdmabufDriver::new();
172
173 if driver_result.is_err() {
175 return;
176 }
177
178 let driver = driver_result.unwrap();
179
180 let start_addr1 = GuestAddress(0x100);
181 let start_addr2 = GuestAddress(0x1100);
182 let start_addr3 = GuestAddress(0x2100);
183
184 let sg_list = [
185 (start_addr1, 0x1000),
186 (start_addr2, 0x1000),
187 (start_addr3, 0x1000),
188 ];
189
190 let mem = GuestMemory::new(&sg_list[..]).unwrap();
191
192 let mut udmabuf_create_list = vec![
193 (start_addr3, 0x1000),
194 (start_addr2, 0x1000),
195 (start_addr1, 0x1000),
196 (GuestAddress(0x4000), 0x1000),
197 ];
198
199 let result = driver.create_udmabuf(&mem, &udmabuf_create_list[..]);
200 assert_eq!(result.is_err(), true);
201
202 udmabuf_create_list.pop();
203
204 let _ = driver
205 .create_udmabuf(&mem, &udmabuf_create_list[..])
206 .unwrap();
207
208 udmabuf_create_list.pop();
209
210 let _ = driver
212 .create_udmabuf(&mem, &udmabuf_create_list[..])
213 .unwrap();
214 }
215}