1use std::convert::TryInto;
8use std::fmt;
9
10use base::linux::MemoryMappingBuilderUnix;
11use base::FromRawDescriptor;
12use base::IntoRawDescriptor;
13use base::MemoryMappingArena;
14use base::MemoryMappingBuilder;
15use base::MmapError;
16use base::SafeDescriptor;
17use thiserror::Error as ThisError;
18use vm_memory::GuestAddress;
19use vm_memory::GuestMemory;
20use vm_memory::GuestMemoryError;
21use zerocopy::FromBytes;
22use zerocopy::Immutable;
23use zerocopy::IntoBytes;
24use zerocopy::KnownLayout;
25
26use crate::virtio::resource_bridge;
27use crate::virtio::resource_bridge::ResourceBridgeError;
28use crate::virtio::resource_bridge::ResourceInfo;
29use crate::virtio::resource_bridge::ResourceRequest;
30use crate::virtio::video::format::Format;
31use crate::virtio::video::format::FramePlane;
32use crate::virtio::video::params::Params;
33use crate::virtio::video::protocol::virtio_video_mem_entry;
34use crate::virtio::video::protocol::virtio_video_object_entry;
35
36#[derive(Clone, Copy, Debug, Default, Eq, PartialEq)]
38pub enum ResourceType {
39 GuestPages,
41 #[default]
43 VirtioObject,
44}
45
46#[repr(C)]
47#[derive(Clone, Copy, FromBytes, Immutable, IntoBytes, KnownLayout)]
48pub struct UnresolvedResourceEntry([u8; 16]);
50
51impl fmt::Debug for UnresolvedResourceEntry {
52 fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
53 write!(f, "unresolved {:?}", self.0)
54 }
55}
56
57impl UnresolvedResourceEntry {
58 pub fn object(&self) -> virtio_video_object_entry {
59 virtio_video_object_entry::read_from_bytes(&self.0).unwrap()
60 }
61}
62
63pub trait BufferHandle: Sized {
65 fn try_clone(&self) -> Result<Self, base::Error>;
68
69 fn get_mapping(&self, offset: usize, size: usize) -> Result<MemoryMappingArena, MmapError>;
71}
72
73#[derive(Clone)]
75pub struct GuestMemArea {
76 pub offset: u64,
78 pub length: usize,
80}
81
82pub struct GuestMemHandle {
83 pub desc: SafeDescriptor,
85 pub mem_areas: Vec<GuestMemArea>,
87}
88
89impl BufferHandle for GuestMemHandle {
90 fn try_clone(&self) -> Result<Self, base::Error> {
91 Ok(Self {
92 desc: self.desc.try_clone()?,
93 mem_areas: self.mem_areas.clone(),
94 })
95 }
96
97 fn get_mapping(&self, offset: usize, size: usize) -> Result<MemoryMappingArena, MmapError> {
98 let mut arena = MemoryMappingArena::new(size)?;
99 let mut mapped_size = 0;
100 let mut area_iter = self.mem_areas.iter();
101 let mut area_offset = offset;
102 while mapped_size < size {
103 let area = match area_iter.next() {
104 Some(area) => area,
105 None => {
106 return Err(MmapError::InvalidRange(
107 offset,
108 size,
109 self.mem_areas.iter().map(|a| a.length).sum(),
110 ));
111 }
112 };
113 if area_offset > area.length {
114 area_offset -= area.length;
115 } else {
116 let mapping_length = std::cmp::min(area.length - area_offset, size - mapped_size);
117 arena.add_fd_offset(mapped_size, mapping_length, &self.desc, area.offset)?;
118 mapped_size += mapping_length;
119 area_offset = 0;
120 }
121 }
122 Ok(arena)
123 }
124}
125
126pub struct VirtioObjectHandle {
127 pub desc: SafeDescriptor,
129 pub modifier: u64,
131}
132
133impl BufferHandle for VirtioObjectHandle {
134 fn try_clone(&self) -> Result<Self, base::Error> {
135 Ok(Self {
136 desc: self.desc.try_clone()?,
137 modifier: self.modifier,
138 })
139 }
140
141 fn get_mapping(&self, offset: usize, size: usize) -> Result<MemoryMappingArena, MmapError> {
142 MemoryMappingBuilder::new(size)
143 .from_descriptor(&self.desc)
144 .offset(offset as u64)
145 .build()
146 .map(MemoryMappingArena::from)
147 }
148}
149
150pub enum GuestResourceHandle {
151 GuestPages(GuestMemHandle),
152 VirtioObject(VirtioObjectHandle),
153}
154
155impl BufferHandle for GuestResourceHandle {
156 fn try_clone(&self) -> Result<Self, base::Error> {
157 Ok(match self {
158 Self::GuestPages(handle) => Self::GuestPages(handle.try_clone()?),
159 Self::VirtioObject(handle) => Self::VirtioObject(handle.try_clone()?),
160 })
161 }
162
163 fn get_mapping(&self, offset: usize, size: usize) -> Result<MemoryMappingArena, MmapError> {
164 match self {
165 GuestResourceHandle::GuestPages(handle) => handle.get_mapping(offset, size),
166 GuestResourceHandle::VirtioObject(handle) => handle.get_mapping(offset, size),
167 }
168 }
169}
170
171pub struct GuestResource {
172 pub handle: GuestResourceHandle,
174 pub planes: Vec<FramePlane>,
176 pub width: u32,
177 pub height: u32,
178 pub format: Format,
179 pub guest_cpu_mappable: bool,
182}
183
184#[derive(Debug, ThisError)]
185pub enum GuestMemResourceCreationError {
186 #[error("Provided slice of entries is empty")]
187 NoEntriesProvided,
188 #[error("cannot get shm region: {0}")]
189 CantGetShmRegion(GuestMemoryError),
190 #[error("cannot get shm offset: {0}")]
191 CantGetShmOffset(GuestMemoryError),
192 #[error("error while cloning shm region descriptor: {0}")]
193 DescriptorCloneError(base::Error),
194 #[error("guest memory with multiple shm objects not supported")]
195 MultipleShmObjects,
196}
197
198#[derive(Debug, ThisError)]
199pub enum ObjectResourceCreationError {
200 #[error("uuid {0:08} is larger than 32 bits")]
201 UuidNot32Bits(u128),
202 #[error("resource returned by bridge is not a buffer")]
203 NotABuffer,
204 #[error("resource bridge failure: {0}")]
205 ResourceBridgeFailure(ResourceBridgeError),
206}
207
208impl GuestResource {
209 pub fn from_virtio_guest_mem_entry(
218 mem_entries: &[virtio_video_mem_entry],
219 mem: &GuestMemory,
220 params: &Params,
221 ) -> Result<GuestResource, GuestMemResourceCreationError> {
222 let region_desc = match mem_entries.first() {
223 None => return Err(GuestMemResourceCreationError::NoEntriesProvided),
224 Some(entry) => {
225 let addr: u64 = entry.addr.into();
226
227 mem.shm_region(GuestAddress(addr))
228 .map_err(GuestMemResourceCreationError::CantGetShmRegion)?
229 }
230 };
231
232 let mem_areas = mem_entries
233 .iter()
234 .map(|entry| {
235 let addr: u64 = entry.addr.into();
236 let length: u32 = entry.length.into();
237 let (backing_obj, region_offset) = mem
238 .offset_from_base(GuestAddress(addr))
239 .map_err(GuestMemResourceCreationError::CantGetShmOffset)
240 .unwrap();
241 if region_desc.as_raw_descriptor() != backing_obj.as_raw_descriptor() {
242 return Err(GuestMemResourceCreationError::MultipleShmObjects);
243 }
244
245 Ok(GuestMemArea {
246 offset: region_offset,
247 length: length as usize,
248 })
249 })
250 .collect::<Result<_, _>>()?;
251
252 let handle = GuestResourceHandle::GuestPages(GuestMemHandle {
253 desc: base::clone_descriptor(region_desc)
254 .map_err(GuestMemResourceCreationError::DescriptorCloneError)?,
255 mem_areas,
256 });
257
258 let mut buffer_offset = 0;
260 let planes = params
261 .plane_formats
262 .iter()
263 .map(|p| {
264 let plane_offset = buffer_offset;
265 buffer_offset += p.plane_size;
266
267 FramePlane {
268 offset: plane_offset as usize,
269 stride: p.stride as usize,
270 size: p.plane_size as usize,
271 }
272 })
273 .collect();
274
275 Ok(GuestResource {
276 handle,
277 planes,
278 width: params.frame_width,
279 height: params.frame_height,
280 format: params.format.unwrap(),
281 guest_cpu_mappable: true,
282 })
283 }
284
285 pub fn from_virtio_object_entry(
291 object: virtio_video_object_entry,
292 res_bridge: &base::Tube,
293 params: &Params,
294 ) -> Result<GuestResource, ObjectResourceCreationError> {
295 let uuid = u128::from_be_bytes(object.uuid);
297
298 let handle = TryInto::<u32>::try_into(uuid)
302 .map_err(|_| ObjectResourceCreationError::UuidNot32Bits(uuid))?;
303
304 let buffer_info = match resource_bridge::get_resource_info(
305 res_bridge,
306 ResourceRequest::GetBuffer { id: handle },
307 ) {
308 Ok(ResourceInfo::Buffer(buffer_info)) => buffer_info,
309 Ok(_) => return Err(ObjectResourceCreationError::NotABuffer),
310 Err(e) => return Err(ObjectResourceCreationError::ResourceBridgeFailure(e)),
311 };
312
313 let handle = GuestResourceHandle::VirtioObject(VirtioObjectHandle {
314 desc: unsafe {
318 SafeDescriptor::from_raw_descriptor(buffer_info.handle.into_raw_descriptor())
319 },
320 modifier: buffer_info.modifier,
321 });
322
323 let planes = params
333 .plane_formats
334 .iter()
335 .zip(&buffer_info.planes)
336 .map(|(param, buffer)| FramePlane {
337 offset: buffer.offset as usize,
342 stride: buffer.stride as usize,
343 size: param.plane_size as usize,
344 })
345 .collect();
346
347 Ok(GuestResource {
348 handle,
349 planes,
350 width: params.frame_width,
351 height: params.frame_height,
352 format: params.format.unwrap(),
353 guest_cpu_mappable: buffer_info.guest_cpu_mappable,
354 })
355 }
356
357 #[cfg(feature = "video-encoder")]
358 pub fn try_clone(&self) -> Result<Self, base::Error> {
359 Ok(Self {
360 handle: self.handle.try_clone()?,
361 planes: self.planes.clone(),
362 width: self.width,
363 height: self.height,
364 format: self.format,
365 guest_cpu_mappable: self.guest_cpu_mappable,
366 })
367 }
368}
369
370#[cfg(test)]
371mod tests {
372 use base::MappedRegion;
373 use base::SharedMemory;
374
375 use super::*;
376
377 fn check_guest_mem_handle(page_order: &[usize]) {
385 const PAGE_SIZE: usize = 0x1000;
386 const U32_SIZE: usize = std::mem::size_of::<u32>();
387 const ENTRIES_PER_PAGE: usize = PAGE_SIZE / std::mem::size_of::<u32>();
388
389 let mut data = vec![0u8; PAGE_SIZE * page_order.len()];
392 for (page_index, page) in page_order.iter().enumerate() {
393 let page_slice = &mut data[(page * PAGE_SIZE)..((page + 1) * PAGE_SIZE)];
394 for (index, chunk) in page_slice.chunks_exact_mut(4).enumerate() {
395 let sized_chunk: &mut [u8; 4] = chunk.try_into().unwrap();
396 *sized_chunk = (((page_index * ENTRIES_PER_PAGE) + index) as u32).to_ne_bytes();
397 }
398 }
399
400 let mem = SharedMemory::new("data-dest", data.len() as u64).unwrap();
402 let mapping = MemoryMappingBuilder::new(mem.size() as usize)
403 .from_shared_memory(&mem)
404 .build()
405 .unwrap();
406 assert_eq!(mapping.write_slice(&data, 0).unwrap(), data.len());
407
408 let mem_handle = GuestResourceHandle::GuestPages(GuestMemHandle {
410 desc: base::clone_descriptor(&mem).unwrap(),
411 mem_areas: page_order
412 .iter()
413 .map(|&page| GuestMemArea {
414 offset: page as u64 * PAGE_SIZE as u64,
415 length: PAGE_SIZE,
416 })
417 .collect(),
418 });
419
420 let mapping = mem_handle.get_mapping(0, mem.size() as usize).unwrap();
423 let mut data = vec![0u8; PAGE_SIZE * page_order.len()];
424 unsafe { std::ptr::copy_nonoverlapping(mapping.as_ptr(), data.as_mut_ptr(), data.len()) };
426 for (index, chunk) in data.chunks_exact(U32_SIZE).enumerate() {
427 let sized_chunk: &[u8; 4] = chunk.try_into().unwrap();
428 assert_eq!(u32::from_ne_bytes(*sized_chunk), index as u32);
429 }
430 }
431
432 #[test]
435 fn test_single_guest_mem_handle() {
436 check_guest_mem_handle(&[0])
437 }
438
439 #[test]
442 fn test_linear_guest_mem_handle() {
443 check_guest_mem_handle(&[0, 1, 2, 3])
444 }
445
446 #[test]
449 fn test_sparse_guest_mem_handle() {
450 check_guest_mem_handle(&[1, 7, 6, 3, 5, 0, 4, 2])
451 }
452}