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 .open(path)
83 .map_err(UdmabufError::DriverOpenFailed)?;
84
85 Ok(UnixUdmabufDriver { driver_fd: fd })
86 }
87
88 fn create_udmabuf(
89 &self,
90 mem: &GuestMemory,
91 iovecs: &[(GuestAddress, usize)],
92 ) -> UdmabufResult<SafeDescriptor> {
93 let pgsize = pagesize();
94
95 let mut list =
96 udmabuf_create_list::<[udmabuf_create_item]>::new_box_zeroed_with_elems(iovecs.len())
97 .unwrap();
98 list.flags = UDMABUF_FLAGS_CLOEXEC;
99 list.count = iovecs.len().try_into().unwrap();
100 for (i, &(addr, len)) in iovecs.iter().enumerate() {
101 let offset = memory_offset(mem, addr, len as u64)?;
102
103 if offset as usize % pgsize != 0 || len % pgsize != 0 {
104 return Err(UdmabufError::NotPageAligned);
105 }
106
107 list.list[i].memfd = mem.shm_region(addr).unwrap().as_raw_descriptor() as u32;
109 list.list[i].__pad = 0;
110 list.list[i].offset = offset;
111 list.list[i].size = len as u64;
112 }
113
114 let fd = unsafe { ioctl_with_ref(&self.driver_fd, UDMABUF_CREATE_LIST, &*list) };
117
118 if fd < 0 {
119 return Err(UdmabufError::DmabufCreationFail(IoError::last_os_error()));
120 }
121
122 Ok(unsafe { SafeDescriptor::from_raw_descriptor(fd) })
125 }
126}
127
128#[cfg(test)]
129mod tests {
130 use super::*;
131 use crate::GuestAddress;
132
133 #[test]
134 fn test_memory_offsets() {
135 let start_addr1 = GuestAddress(0x100);
136 let start_addr2 = GuestAddress(0x100 + pagesize() as u64);
137 let start_addr3 = GuestAddress(0x100 + 2 * pagesize() as u64);
138
139 let mem = GuestMemory::new(&[
140 (start_addr1, pagesize() as u64),
141 (start_addr2, pagesize() as u64),
142 (start_addr3, pagesize() as u64),
143 ])
144 .unwrap();
145
146 assert_eq!(memory_offset(&mem, GuestAddress(0x300), 1).unwrap(), 0x200);
147 assert_eq!(
148 memory_offset(&mem, GuestAddress(0x200 + pagesize() as u64), 1).unwrap(),
149 0x100 + pagesize() as u64,
150 );
151 assert_eq!(
152 memory_offset(
153 &mem,
154 GuestAddress(0x100 + pagesize() as u64),
155 pagesize() as u64
156 )
157 .unwrap(),
158 pagesize() as u64,
159 );
160 assert!(memory_offset(
161 &mem,
162 GuestAddress(0x100 + pagesize() as u64),
163 1 + pagesize() as u64
164 )
165 .is_err());
166 }
167
168 #[test]
169 fn test_udmabuf_create() {
170 let driver_result = UnixUdmabufDriver::new();
171
172 if driver_result.is_err() {
174 return;
175 }
176
177 let driver = driver_result.unwrap();
178
179 let start_addr1 = GuestAddress(0x100);
180 let start_addr2 = GuestAddress(0x1100);
181 let start_addr3 = GuestAddress(0x2100);
182
183 let sg_list = [
184 (start_addr1, 0x1000),
185 (start_addr2, 0x1000),
186 (start_addr3, 0x1000),
187 ];
188
189 let mem = GuestMemory::new(&sg_list[..]).unwrap();
190
191 let mut udmabuf_create_list = vec![
192 (start_addr3, 0x1000),
193 (start_addr2, 0x1000),
194 (start_addr1, 0x1000),
195 (GuestAddress(0x4000), 0x1000),
196 ];
197
198 let result = driver.create_udmabuf(&mem, &udmabuf_create_list[..]);
199 assert_eq!(result.is_err(), true);
200
201 udmabuf_create_list.pop();
202
203 let _ = driver
204 .create_udmabuf(&mem, &udmabuf_create_list[..])
205 .unwrap();
206
207 udmabuf_create_list.pop();
208
209 let _ = driver
211 .create_udmabuf(&mem, &udmabuf_create_list[..])
212 .unwrap();
213 }
214}