1 // Copyright 2021 The ChromiumOS Authors
2 // Use of this source code is governed by a BSD-style license that can be
3 // found in the LICENSE file.
4
5 #![allow(dead_code)]
6
7 use std::fs::File;
8 use std::fs::OpenOptions;
9 use std::io::Error as IoError;
10 use std::os::raw::c_uint;
11 use std::path::Path;
12
13 use base::ioctl_iow_nr;
14 use base::ioctl_with_ptr;
15 use base::pagesize;
16 use base::FromRawDescriptor;
17 use base::MappedRegion;
18 use base::SafeDescriptor;
19 use data_model::flexible_array_impl;
20 use data_model::FlexibleArrayWrapper;
21
22 use crate::udmabuf::UdmabufDriverTrait;
23 use crate::udmabuf::UdmabufError;
24 use crate::udmabuf::UdmabufResult;
25 use crate::udmabuf_bindings::*;
26 use crate::GuestAddress;
27 use crate::GuestMemory;
28 use crate::GuestMemoryError;
29
30 const UDMABUF_IOCTL_BASE: c_uint = 0x75;
31
32 ioctl_iow_nr!(UDMABUF_CREATE, UDMABUF_IOCTL_BASE, 0x42, udmabuf_create);
33 ioctl_iow_nr!(
34 UDMABUF_CREATE_LIST,
35 UDMABUF_IOCTL_BASE,
36 0x43,
37 udmabuf_create_list
38 );
39
40 flexible_array_impl!(udmabuf_create_list, udmabuf_create_item, count, list);
41 type UdmabufCreateList = FlexibleArrayWrapper<udmabuf_create_list, udmabuf_create_item>;
42
43 // Returns absolute offset within the memory corresponding to a particular guest address.
44 // This offset is not relative to a particular mapping.
45
46 // # Examples
47 //
48 // # fn test_memory_offsets() {
49 // # let start_addr1 = GuestAddress(0x100)
50 // # let start_addr2 = GuestAddress(0x1100);
51 // # let mem = GuestMemory::new(&vec![(start_addr1, 0x1000),(start_addr2, 0x1000)])?;
52 // # assert_eq!(memory_offset(&mem, GuestAddress(0x1100), 0x1000).unwrap(),0x1000);
53 // #}
memory_offset(mem: &GuestMemory, guest_addr: GuestAddress, len: u64) -> UdmabufResult<u64>54 fn memory_offset(mem: &GuestMemory, guest_addr: GuestAddress, len: u64) -> UdmabufResult<u64> {
55 let (mapping, map_offset, memfd_offset) = mem
56 .find_region(guest_addr)
57 .map_err(UdmabufError::InvalidOffset)?;
58 let map_offset = map_offset as u64;
59 if map_offset
60 .checked_add(len)
61 .map_or(true, |a| a > mapping.size() as u64)
62 {
63 return Err(UdmabufError::InvalidOffset(
64 GuestMemoryError::InvalidGuestAddress(guest_addr),
65 ));
66 }
67
68 Ok(memfd_offset + map_offset)
69 }
70
71 /// A convenience wrapper for the Linux kernel's udmabuf driver.
72 ///
73 /// udmabuf is a kernel driver that turns memfd pages into dmabufs. It can be used for
74 /// zero-copy buffer sharing between the guest and host when guest memory is backed by
75 /// memfd pages.
76 pub struct UnixUdmabufDriver {
77 driver_fd: File,
78 }
79
80 impl UdmabufDriverTrait for UnixUdmabufDriver {
new() -> UdmabufResult<UnixUdmabufDriver>81 fn new() -> UdmabufResult<UnixUdmabufDriver> {
82 const UDMABUF_PATH: &str = "/dev/udmabuf";
83 let path = Path::new(UDMABUF_PATH);
84 let fd = OpenOptions::new()
85 .read(true)
86 .write(true)
87 .open(path)
88 .map_err(UdmabufError::DriverOpenFailed)?;
89
90 Ok(UnixUdmabufDriver { driver_fd: fd })
91 }
92
create_udmabuf( &self, mem: &GuestMemory, iovecs: &[(GuestAddress, usize)], ) -> UdmabufResult<SafeDescriptor>93 fn create_udmabuf(
94 &self,
95 mem: &GuestMemory,
96 iovecs: &[(GuestAddress, usize)],
97 ) -> UdmabufResult<SafeDescriptor> {
98 let pgsize = pagesize();
99
100 let mut list = UdmabufCreateList::new(iovecs.len());
101 let items = list.mut_entries_slice();
102 for (i, &(addr, len)) in iovecs.iter().enumerate() {
103 let offset = memory_offset(mem, addr, len as u64)?;
104
105 if offset as usize % pgsize != 0 || len % pgsize != 0 {
106 return Err(UdmabufError::NotPageAligned);
107 }
108
109 // `unwrap` can't panic if `memory_offset obove succeeds.
110 items[i].memfd = mem.shm_region(addr).unwrap().as_raw_descriptor() as u32;
111 items[i].__pad = 0;
112 items[i].offset = offset;
113 items[i].size = len as u64;
114 }
115
116 // SAFETY:
117 // Safe because we always allocate enough space for `udmabuf_create_list`.
118 let fd = unsafe {
119 let create_list = list.as_mut_ptr();
120 (*create_list).flags = UDMABUF_FLAGS_CLOEXEC;
121 ioctl_with_ptr(&self.driver_fd, UDMABUF_CREATE_LIST, create_list)
122 };
123
124 if fd < 0 {
125 return Err(UdmabufError::DmabufCreationFail(IoError::last_os_error()));
126 }
127
128 // SAFETY:
129 // Safe because we validated the file exists.
130 Ok(unsafe { SafeDescriptor::from_raw_descriptor(fd) })
131 }
132 }
133
134 #[cfg(test)]
135 mod tests {
136 use super::*;
137 use crate::GuestAddress;
138
139 #[test]
test_memory_offsets()140 fn test_memory_offsets() {
141 let start_addr1 = GuestAddress(0x100);
142 let start_addr2 = GuestAddress(0x100 + pagesize() as u64);
143 let start_addr3 = GuestAddress(0x100 + 2 * pagesize() as u64);
144
145 let mem = GuestMemory::new(&[
146 (start_addr1, pagesize() as u64),
147 (start_addr2, pagesize() as u64),
148 (start_addr3, pagesize() as u64),
149 ])
150 .unwrap();
151
152 assert_eq!(memory_offset(&mem, GuestAddress(0x300), 1).unwrap(), 0x200);
153 assert_eq!(
154 memory_offset(&mem, GuestAddress(0x200 + pagesize() as u64), 1).unwrap(),
155 0x100 + pagesize() as u64,
156 );
157 assert_eq!(
158 memory_offset(
159 &mem,
160 GuestAddress(0x100 + pagesize() as u64),
161 pagesize() as u64
162 )
163 .unwrap(),
164 pagesize() as u64,
165 );
166 assert!(memory_offset(
167 &mem,
168 GuestAddress(0x100 + pagesize() as u64),
169 1 + pagesize() as u64
170 )
171 .is_err());
172 }
173
174 #[test]
test_udmabuf_create()175 fn test_udmabuf_create() {
176 let driver_result = UnixUdmabufDriver::new();
177
178 // Most kernels will not have udmabuf support.
179 if driver_result.is_err() {
180 return;
181 }
182
183 let driver = driver_result.unwrap();
184
185 let start_addr1 = GuestAddress(0x100);
186 let start_addr2 = GuestAddress(0x1100);
187 let start_addr3 = GuestAddress(0x2100);
188
189 let sg_list = [
190 (start_addr1, 0x1000),
191 (start_addr2, 0x1000),
192 (start_addr3, 0x1000),
193 ];
194
195 let mem = GuestMemory::new(&sg_list[..]).unwrap();
196
197 let mut udmabuf_create_list = vec![
198 (start_addr3, 0x1000),
199 (start_addr2, 0x1000),
200 (start_addr1, 0x1000),
201 (GuestAddress(0x4000), 0x1000),
202 ];
203
204 let result = driver.create_udmabuf(&mem, &udmabuf_create_list[..]);
205 assert_eq!(result.is_err(), true);
206
207 udmabuf_create_list.pop();
208
209 let _ = driver
210 .create_udmabuf(&mem, &udmabuf_create_list[..])
211 .unwrap();
212
213 udmabuf_create_list.pop();
214
215 // Multiple udmabufs with same memory backing is allowed.
216 let _ = driver
217 .create_udmabuf(&mem, &udmabuf_create_list[..])
218 .unwrap();
219 }
220 }
221