xref: /aosp_15_r20/external/crosvm/devices/src/virtio/video/resource.rs (revision bb4ee6a4ae7042d18b07a98463b9c8b875e44b39)
1*bb4ee6a4SAndroid Build Coastguard Worker // Copyright 2021 The ChromiumOS Authors
2*bb4ee6a4SAndroid Build Coastguard Worker // Use of this source code is governed by a BSD-style license that can be
3*bb4ee6a4SAndroid Build Coastguard Worker // found in the LICENSE file.
4*bb4ee6a4SAndroid Build Coastguard Worker 
5*bb4ee6a4SAndroid Build Coastguard Worker //! Resource management and resolution for the virtio-video device.
6*bb4ee6a4SAndroid Build Coastguard Worker 
7*bb4ee6a4SAndroid Build Coastguard Worker use std::convert::TryInto;
8*bb4ee6a4SAndroid Build Coastguard Worker use std::fmt;
9*bb4ee6a4SAndroid Build Coastguard Worker 
10*bb4ee6a4SAndroid Build Coastguard Worker use base::linux::MemoryMappingBuilderUnix;
11*bb4ee6a4SAndroid Build Coastguard Worker use base::FromRawDescriptor;
12*bb4ee6a4SAndroid Build Coastguard Worker use base::IntoRawDescriptor;
13*bb4ee6a4SAndroid Build Coastguard Worker use base::MemoryMappingArena;
14*bb4ee6a4SAndroid Build Coastguard Worker use base::MemoryMappingBuilder;
15*bb4ee6a4SAndroid Build Coastguard Worker use base::MmapError;
16*bb4ee6a4SAndroid Build Coastguard Worker use base::SafeDescriptor;
17*bb4ee6a4SAndroid Build Coastguard Worker use thiserror::Error as ThisError;
18*bb4ee6a4SAndroid Build Coastguard Worker use vm_memory::GuestAddress;
19*bb4ee6a4SAndroid Build Coastguard Worker use vm_memory::GuestMemory;
20*bb4ee6a4SAndroid Build Coastguard Worker use vm_memory::GuestMemoryError;
21*bb4ee6a4SAndroid Build Coastguard Worker use zerocopy::AsBytes;
22*bb4ee6a4SAndroid Build Coastguard Worker use zerocopy::FromBytes;
23*bb4ee6a4SAndroid Build Coastguard Worker use zerocopy::FromZeroes;
24*bb4ee6a4SAndroid Build Coastguard Worker 
25*bb4ee6a4SAndroid Build Coastguard Worker use crate::virtio::resource_bridge;
26*bb4ee6a4SAndroid Build Coastguard Worker use crate::virtio::resource_bridge::ResourceBridgeError;
27*bb4ee6a4SAndroid Build Coastguard Worker use crate::virtio::resource_bridge::ResourceInfo;
28*bb4ee6a4SAndroid Build Coastguard Worker use crate::virtio::resource_bridge::ResourceRequest;
29*bb4ee6a4SAndroid Build Coastguard Worker use crate::virtio::video::format::Format;
30*bb4ee6a4SAndroid Build Coastguard Worker use crate::virtio::video::format::FramePlane;
31*bb4ee6a4SAndroid Build Coastguard Worker use crate::virtio::video::params::Params;
32*bb4ee6a4SAndroid Build Coastguard Worker use crate::virtio::video::protocol::virtio_video_mem_entry;
33*bb4ee6a4SAndroid Build Coastguard Worker use crate::virtio::video::protocol::virtio_video_object_entry;
34*bb4ee6a4SAndroid Build Coastguard Worker 
35*bb4ee6a4SAndroid Build Coastguard Worker /// Defines how resources for a given queue are represented.
36*bb4ee6a4SAndroid Build Coastguard Worker #[derive(Clone, Copy, Debug, Default, Eq, PartialEq)]
37*bb4ee6a4SAndroid Build Coastguard Worker pub enum ResourceType {
38*bb4ee6a4SAndroid Build Coastguard Worker     /// Resources are backed by guest memory pages.
39*bb4ee6a4SAndroid Build Coastguard Worker     GuestPages,
40*bb4ee6a4SAndroid Build Coastguard Worker     /// Resources are backed by virtio objects.
41*bb4ee6a4SAndroid Build Coastguard Worker     #[default]
42*bb4ee6a4SAndroid Build Coastguard Worker     VirtioObject,
43*bb4ee6a4SAndroid Build Coastguard Worker }
44*bb4ee6a4SAndroid Build Coastguard Worker 
45*bb4ee6a4SAndroid Build Coastguard Worker #[repr(C)]
46*bb4ee6a4SAndroid Build Coastguard Worker #[derive(Clone, Copy, AsBytes, FromZeroes, FromBytes)]
47*bb4ee6a4SAndroid Build Coastguard Worker /// A guest resource entry which type is not decided yet.
48*bb4ee6a4SAndroid Build Coastguard Worker pub union UnresolvedResourceEntry {
49*bb4ee6a4SAndroid Build Coastguard Worker     pub object: virtio_video_object_entry,
50*bb4ee6a4SAndroid Build Coastguard Worker     pub guest_mem: virtio_video_mem_entry,
51*bb4ee6a4SAndroid Build Coastguard Worker }
52*bb4ee6a4SAndroid Build Coastguard Worker 
53*bb4ee6a4SAndroid Build Coastguard Worker impl fmt::Debug for UnresolvedResourceEntry {
fmt(&self, f: &mut fmt::Formatter) -> fmt::Result54*bb4ee6a4SAndroid Build Coastguard Worker     fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
55*bb4ee6a4SAndroid Build Coastguard Worker         write!(
56*bb4ee6a4SAndroid Build Coastguard Worker             f,
57*bb4ee6a4SAndroid Build Coastguard Worker             "unresolved {:?} or {:?}",
58*bb4ee6a4SAndroid Build Coastguard Worker             // SAFETY:
59*bb4ee6a4SAndroid Build Coastguard Worker             // Safe because `self.object` and `self.guest_mem` are the same size and both made of
60*bb4ee6a4SAndroid Build Coastguard Worker             // integers, making it safe to display them no matter their value.
61*bb4ee6a4SAndroid Build Coastguard Worker             unsafe { self.object },
62*bb4ee6a4SAndroid Build Coastguard Worker             // SAFETY:
63*bb4ee6a4SAndroid Build Coastguard Worker             // Safe because `self.object` and `self.guest_mem` are the same size and both made of
64*bb4ee6a4SAndroid Build Coastguard Worker             // integers, making it safe to display them no matter their value.
65*bb4ee6a4SAndroid Build Coastguard Worker             unsafe { self.guest_mem }
66*bb4ee6a4SAndroid Build Coastguard Worker         )
67*bb4ee6a4SAndroid Build Coastguard Worker     }
68*bb4ee6a4SAndroid Build Coastguard Worker }
69*bb4ee6a4SAndroid Build Coastguard Worker 
70*bb4ee6a4SAndroid Build Coastguard Worker /// Trait for types that can serve as video buffer backing memory.
71*bb4ee6a4SAndroid Build Coastguard Worker pub trait BufferHandle: Sized {
72*bb4ee6a4SAndroid Build Coastguard Worker     /// Try to clone this handle. This must only create a new reference to the same backing memory
73*bb4ee6a4SAndroid Build Coastguard Worker     /// and not duplicate the buffer itself.
try_clone(&self) -> Result<Self, base::Error>74*bb4ee6a4SAndroid Build Coastguard Worker     fn try_clone(&self) -> Result<Self, base::Error>;
75*bb4ee6a4SAndroid Build Coastguard Worker 
76*bb4ee6a4SAndroid Build Coastguard Worker     /// Returns a linear mapping of [`offset`..`offset`+`size`] of the memory backing this buffer.
get_mapping(&self, offset: usize, size: usize) -> Result<MemoryMappingArena, MmapError>77*bb4ee6a4SAndroid Build Coastguard Worker     fn get_mapping(&self, offset: usize, size: usize) -> Result<MemoryMappingArena, MmapError>;
78*bb4ee6a4SAndroid Build Coastguard Worker }
79*bb4ee6a4SAndroid Build Coastguard Worker 
80*bb4ee6a4SAndroid Build Coastguard Worker /// Linear memory area of a `GuestMemHandle`
81*bb4ee6a4SAndroid Build Coastguard Worker #[derive(Clone)]
82*bb4ee6a4SAndroid Build Coastguard Worker pub struct GuestMemArea {
83*bb4ee6a4SAndroid Build Coastguard Worker     /// Offset within the guest region to the start of the area.
84*bb4ee6a4SAndroid Build Coastguard Worker     pub offset: u64,
85*bb4ee6a4SAndroid Build Coastguard Worker     /// Length of the area within the memory region.
86*bb4ee6a4SAndroid Build Coastguard Worker     pub length: usize,
87*bb4ee6a4SAndroid Build Coastguard Worker }
88*bb4ee6a4SAndroid Build Coastguard Worker 
89*bb4ee6a4SAndroid Build Coastguard Worker pub struct GuestMemHandle {
90*bb4ee6a4SAndroid Build Coastguard Worker     /// Descriptor to the guest memory region containing the buffer.
91*bb4ee6a4SAndroid Build Coastguard Worker     pub desc: SafeDescriptor,
92*bb4ee6a4SAndroid Build Coastguard Worker     /// Memory areas (i.e. sg list) that make the memory buffer.
93*bb4ee6a4SAndroid Build Coastguard Worker     pub mem_areas: Vec<GuestMemArea>,
94*bb4ee6a4SAndroid Build Coastguard Worker }
95*bb4ee6a4SAndroid Build Coastguard Worker 
96*bb4ee6a4SAndroid Build Coastguard Worker impl BufferHandle for GuestMemHandle {
try_clone(&self) -> Result<Self, base::Error>97*bb4ee6a4SAndroid Build Coastguard Worker     fn try_clone(&self) -> Result<Self, base::Error> {
98*bb4ee6a4SAndroid Build Coastguard Worker         Ok(Self {
99*bb4ee6a4SAndroid Build Coastguard Worker             desc: self.desc.try_clone()?,
100*bb4ee6a4SAndroid Build Coastguard Worker             mem_areas: self.mem_areas.clone(),
101*bb4ee6a4SAndroid Build Coastguard Worker         })
102*bb4ee6a4SAndroid Build Coastguard Worker     }
103*bb4ee6a4SAndroid Build Coastguard Worker 
get_mapping(&self, offset: usize, size: usize) -> Result<MemoryMappingArena, MmapError>104*bb4ee6a4SAndroid Build Coastguard Worker     fn get_mapping(&self, offset: usize, size: usize) -> Result<MemoryMappingArena, MmapError> {
105*bb4ee6a4SAndroid Build Coastguard Worker         let mut arena = MemoryMappingArena::new(size)?;
106*bb4ee6a4SAndroid Build Coastguard Worker         let mut mapped_size = 0;
107*bb4ee6a4SAndroid Build Coastguard Worker         let mut area_iter = self.mem_areas.iter();
108*bb4ee6a4SAndroid Build Coastguard Worker         let mut area_offset = offset;
109*bb4ee6a4SAndroid Build Coastguard Worker         while mapped_size < size {
110*bb4ee6a4SAndroid Build Coastguard Worker             let area = match area_iter.next() {
111*bb4ee6a4SAndroid Build Coastguard Worker                 Some(area) => area,
112*bb4ee6a4SAndroid Build Coastguard Worker                 None => {
113*bb4ee6a4SAndroid Build Coastguard Worker                     return Err(MmapError::InvalidRange(
114*bb4ee6a4SAndroid Build Coastguard Worker                         offset,
115*bb4ee6a4SAndroid Build Coastguard Worker                         size,
116*bb4ee6a4SAndroid Build Coastguard Worker                         self.mem_areas.iter().map(|a| a.length).sum(),
117*bb4ee6a4SAndroid Build Coastguard Worker                     ));
118*bb4ee6a4SAndroid Build Coastguard Worker                 }
119*bb4ee6a4SAndroid Build Coastguard Worker             };
120*bb4ee6a4SAndroid Build Coastguard Worker             if area_offset > area.length {
121*bb4ee6a4SAndroid Build Coastguard Worker                 area_offset -= area.length;
122*bb4ee6a4SAndroid Build Coastguard Worker             } else {
123*bb4ee6a4SAndroid Build Coastguard Worker                 let mapping_length = std::cmp::min(area.length - area_offset, size - mapped_size);
124*bb4ee6a4SAndroid Build Coastguard Worker                 arena.add_fd_offset(mapped_size, mapping_length, &self.desc, area.offset)?;
125*bb4ee6a4SAndroid Build Coastguard Worker                 mapped_size += mapping_length;
126*bb4ee6a4SAndroid Build Coastguard Worker                 area_offset = 0;
127*bb4ee6a4SAndroid Build Coastguard Worker             }
128*bb4ee6a4SAndroid Build Coastguard Worker         }
129*bb4ee6a4SAndroid Build Coastguard Worker         Ok(arena)
130*bb4ee6a4SAndroid Build Coastguard Worker     }
131*bb4ee6a4SAndroid Build Coastguard Worker }
132*bb4ee6a4SAndroid Build Coastguard Worker 
133*bb4ee6a4SAndroid Build Coastguard Worker pub struct VirtioObjectHandle {
134*bb4ee6a4SAndroid Build Coastguard Worker     /// Descriptor for the object.
135*bb4ee6a4SAndroid Build Coastguard Worker     pub desc: SafeDescriptor,
136*bb4ee6a4SAndroid Build Coastguard Worker     /// Modifier to apply to frame resources.
137*bb4ee6a4SAndroid Build Coastguard Worker     pub modifier: u64,
138*bb4ee6a4SAndroid Build Coastguard Worker }
139*bb4ee6a4SAndroid Build Coastguard Worker 
140*bb4ee6a4SAndroid Build Coastguard Worker impl BufferHandle for VirtioObjectHandle {
try_clone(&self) -> Result<Self, base::Error>141*bb4ee6a4SAndroid Build Coastguard Worker     fn try_clone(&self) -> Result<Self, base::Error> {
142*bb4ee6a4SAndroid Build Coastguard Worker         Ok(Self {
143*bb4ee6a4SAndroid Build Coastguard Worker             desc: self.desc.try_clone()?,
144*bb4ee6a4SAndroid Build Coastguard Worker             modifier: self.modifier,
145*bb4ee6a4SAndroid Build Coastguard Worker         })
146*bb4ee6a4SAndroid Build Coastguard Worker     }
147*bb4ee6a4SAndroid Build Coastguard Worker 
get_mapping(&self, offset: usize, size: usize) -> Result<MemoryMappingArena, MmapError>148*bb4ee6a4SAndroid Build Coastguard Worker     fn get_mapping(&self, offset: usize, size: usize) -> Result<MemoryMappingArena, MmapError> {
149*bb4ee6a4SAndroid Build Coastguard Worker         MemoryMappingBuilder::new(size)
150*bb4ee6a4SAndroid Build Coastguard Worker             .from_descriptor(&self.desc)
151*bb4ee6a4SAndroid Build Coastguard Worker             .offset(offset as u64)
152*bb4ee6a4SAndroid Build Coastguard Worker             .build()
153*bb4ee6a4SAndroid Build Coastguard Worker             .map(MemoryMappingArena::from)
154*bb4ee6a4SAndroid Build Coastguard Worker     }
155*bb4ee6a4SAndroid Build Coastguard Worker }
156*bb4ee6a4SAndroid Build Coastguard Worker 
157*bb4ee6a4SAndroid Build Coastguard Worker pub enum GuestResourceHandle {
158*bb4ee6a4SAndroid Build Coastguard Worker     GuestPages(GuestMemHandle),
159*bb4ee6a4SAndroid Build Coastguard Worker     VirtioObject(VirtioObjectHandle),
160*bb4ee6a4SAndroid Build Coastguard Worker }
161*bb4ee6a4SAndroid Build Coastguard Worker 
162*bb4ee6a4SAndroid Build Coastguard Worker impl BufferHandle for GuestResourceHandle {
try_clone(&self) -> Result<Self, base::Error>163*bb4ee6a4SAndroid Build Coastguard Worker     fn try_clone(&self) -> Result<Self, base::Error> {
164*bb4ee6a4SAndroid Build Coastguard Worker         Ok(match self {
165*bb4ee6a4SAndroid Build Coastguard Worker             Self::GuestPages(handle) => Self::GuestPages(handle.try_clone()?),
166*bb4ee6a4SAndroid Build Coastguard Worker             Self::VirtioObject(handle) => Self::VirtioObject(handle.try_clone()?),
167*bb4ee6a4SAndroid Build Coastguard Worker         })
168*bb4ee6a4SAndroid Build Coastguard Worker     }
169*bb4ee6a4SAndroid Build Coastguard Worker 
get_mapping(&self, offset: usize, size: usize) -> Result<MemoryMappingArena, MmapError>170*bb4ee6a4SAndroid Build Coastguard Worker     fn get_mapping(&self, offset: usize, size: usize) -> Result<MemoryMappingArena, MmapError> {
171*bb4ee6a4SAndroid Build Coastguard Worker         match self {
172*bb4ee6a4SAndroid Build Coastguard Worker             GuestResourceHandle::GuestPages(handle) => handle.get_mapping(offset, size),
173*bb4ee6a4SAndroid Build Coastguard Worker             GuestResourceHandle::VirtioObject(handle) => handle.get_mapping(offset, size),
174*bb4ee6a4SAndroid Build Coastguard Worker         }
175*bb4ee6a4SAndroid Build Coastguard Worker     }
176*bb4ee6a4SAndroid Build Coastguard Worker }
177*bb4ee6a4SAndroid Build Coastguard Worker 
178*bb4ee6a4SAndroid Build Coastguard Worker pub struct GuestResource {
179*bb4ee6a4SAndroid Build Coastguard Worker     /// Handle to the backing memory.
180*bb4ee6a4SAndroid Build Coastguard Worker     pub handle: GuestResourceHandle,
181*bb4ee6a4SAndroid Build Coastguard Worker     /// Layout of color planes, if the resource will receive frames.
182*bb4ee6a4SAndroid Build Coastguard Worker     pub planes: Vec<FramePlane>,
183*bb4ee6a4SAndroid Build Coastguard Worker     pub width: u32,
184*bb4ee6a4SAndroid Build Coastguard Worker     pub height: u32,
185*bb4ee6a4SAndroid Build Coastguard Worker     pub format: Format,
186*bb4ee6a4SAndroid Build Coastguard Worker     /// Whether the buffer can be accessed by the guest CPU. This means the host must ensure that
187*bb4ee6a4SAndroid Build Coastguard Worker     /// all operations on the buffer are completed before passing it to the guest.
188*bb4ee6a4SAndroid Build Coastguard Worker     pub guest_cpu_mappable: bool,
189*bb4ee6a4SAndroid Build Coastguard Worker }
190*bb4ee6a4SAndroid Build Coastguard Worker 
191*bb4ee6a4SAndroid Build Coastguard Worker #[derive(Debug, ThisError)]
192*bb4ee6a4SAndroid Build Coastguard Worker pub enum GuestMemResourceCreationError {
193*bb4ee6a4SAndroid Build Coastguard Worker     #[error("Provided slice of entries is empty")]
194*bb4ee6a4SAndroid Build Coastguard Worker     NoEntriesProvided,
195*bb4ee6a4SAndroid Build Coastguard Worker     #[error("cannot get shm region: {0}")]
196*bb4ee6a4SAndroid Build Coastguard Worker     CantGetShmRegion(GuestMemoryError),
197*bb4ee6a4SAndroid Build Coastguard Worker     #[error("cannot get shm offset: {0}")]
198*bb4ee6a4SAndroid Build Coastguard Worker     CantGetShmOffset(GuestMemoryError),
199*bb4ee6a4SAndroid Build Coastguard Worker     #[error("error while cloning shm region descriptor: {0}")]
200*bb4ee6a4SAndroid Build Coastguard Worker     DescriptorCloneError(base::Error),
201*bb4ee6a4SAndroid Build Coastguard Worker }
202*bb4ee6a4SAndroid Build Coastguard Worker 
203*bb4ee6a4SAndroid Build Coastguard Worker #[derive(Debug, ThisError)]
204*bb4ee6a4SAndroid Build Coastguard Worker pub enum ObjectResourceCreationError {
205*bb4ee6a4SAndroid Build Coastguard Worker     #[error("uuid {0:08} is larger than 32 bits")]
206*bb4ee6a4SAndroid Build Coastguard Worker     UuidNot32Bits(u128),
207*bb4ee6a4SAndroid Build Coastguard Worker     #[error("resource returned by bridge is not a buffer")]
208*bb4ee6a4SAndroid Build Coastguard Worker     NotABuffer,
209*bb4ee6a4SAndroid Build Coastguard Worker     #[error("resource bridge failure: {0}")]
210*bb4ee6a4SAndroid Build Coastguard Worker     ResourceBridgeFailure(ResourceBridgeError),
211*bb4ee6a4SAndroid Build Coastguard Worker }
212*bb4ee6a4SAndroid Build Coastguard Worker 
213*bb4ee6a4SAndroid Build Coastguard Worker impl GuestResource {
214*bb4ee6a4SAndroid Build Coastguard Worker     /// Try to convert an unresolved virtio guest memory entry into a resolved guest memory
215*bb4ee6a4SAndroid Build Coastguard Worker     /// resource.
216*bb4ee6a4SAndroid Build Coastguard Worker     ///
217*bb4ee6a4SAndroid Build Coastguard Worker     /// Convert `mem_entry` into the guest memory resource it represents and resolve it through
218*bb4ee6a4SAndroid Build Coastguard Worker     /// `mem`.
219*bb4ee6a4SAndroid Build Coastguard Worker     /// Width, height and format is set from `params`.
220*bb4ee6a4SAndroid Build Coastguard Worker     ///
221*bb4ee6a4SAndroid Build Coastguard Worker     /// Panics if `params.format` is `None`.
from_virtio_guest_mem_entry( mem_entries: &[virtio_video_mem_entry], mem: &GuestMemory, params: &Params, ) -> Result<GuestResource, GuestMemResourceCreationError>222*bb4ee6a4SAndroid Build Coastguard Worker     pub fn from_virtio_guest_mem_entry(
223*bb4ee6a4SAndroid Build Coastguard Worker         mem_entries: &[virtio_video_mem_entry],
224*bb4ee6a4SAndroid Build Coastguard Worker         mem: &GuestMemory,
225*bb4ee6a4SAndroid Build Coastguard Worker         params: &Params,
226*bb4ee6a4SAndroid Build Coastguard Worker     ) -> Result<GuestResource, GuestMemResourceCreationError> {
227*bb4ee6a4SAndroid Build Coastguard Worker         let region_desc = match mem_entries.first() {
228*bb4ee6a4SAndroid Build Coastguard Worker             None => return Err(GuestMemResourceCreationError::NoEntriesProvided),
229*bb4ee6a4SAndroid Build Coastguard Worker             Some(entry) => {
230*bb4ee6a4SAndroid Build Coastguard Worker                 let addr: u64 = entry.addr.into();
231*bb4ee6a4SAndroid Build Coastguard Worker 
232*bb4ee6a4SAndroid Build Coastguard Worker                 let guest_region = mem
233*bb4ee6a4SAndroid Build Coastguard Worker                     .shm_region(GuestAddress(addr))
234*bb4ee6a4SAndroid Build Coastguard Worker                     .map_err(GuestMemResourceCreationError::CantGetShmRegion)?;
235*bb4ee6a4SAndroid Build Coastguard Worker                 base::clone_descriptor(guest_region)
236*bb4ee6a4SAndroid Build Coastguard Worker                     .map_err(GuestMemResourceCreationError::DescriptorCloneError)?
237*bb4ee6a4SAndroid Build Coastguard Worker             }
238*bb4ee6a4SAndroid Build Coastguard Worker         };
239*bb4ee6a4SAndroid Build Coastguard Worker 
240*bb4ee6a4SAndroid Build Coastguard Worker         let mem_areas = mem_entries
241*bb4ee6a4SAndroid Build Coastguard Worker             .iter()
242*bb4ee6a4SAndroid Build Coastguard Worker             .map(|entry| {
243*bb4ee6a4SAndroid Build Coastguard Worker                 let addr: u64 = entry.addr.into();
244*bb4ee6a4SAndroid Build Coastguard Worker                 let length: u32 = entry.length.into();
245*bb4ee6a4SAndroid Build Coastguard Worker                 let region_offset = mem
246*bb4ee6a4SAndroid Build Coastguard Worker                     .offset_from_base(GuestAddress(addr))
247*bb4ee6a4SAndroid Build Coastguard Worker                     .map_err(GuestMemResourceCreationError::CantGetShmOffset)
248*bb4ee6a4SAndroid Build Coastguard Worker                     .unwrap();
249*bb4ee6a4SAndroid Build Coastguard Worker 
250*bb4ee6a4SAndroid Build Coastguard Worker                 GuestMemArea {
251*bb4ee6a4SAndroid Build Coastguard Worker                     offset: region_offset,
252*bb4ee6a4SAndroid Build Coastguard Worker                     length: length as usize,
253*bb4ee6a4SAndroid Build Coastguard Worker                 }
254*bb4ee6a4SAndroid Build Coastguard Worker             })
255*bb4ee6a4SAndroid Build Coastguard Worker             .collect();
256*bb4ee6a4SAndroid Build Coastguard Worker 
257*bb4ee6a4SAndroid Build Coastguard Worker         let handle = GuestResourceHandle::GuestPages(GuestMemHandle {
258*bb4ee6a4SAndroid Build Coastguard Worker             desc: region_desc,
259*bb4ee6a4SAndroid Build Coastguard Worker             mem_areas,
260*bb4ee6a4SAndroid Build Coastguard Worker         });
261*bb4ee6a4SAndroid Build Coastguard Worker 
262*bb4ee6a4SAndroid Build Coastguard Worker         // The plane information can be computed from the currently set format.
263*bb4ee6a4SAndroid Build Coastguard Worker         let mut buffer_offset = 0;
264*bb4ee6a4SAndroid Build Coastguard Worker         let planes = params
265*bb4ee6a4SAndroid Build Coastguard Worker             .plane_formats
266*bb4ee6a4SAndroid Build Coastguard Worker             .iter()
267*bb4ee6a4SAndroid Build Coastguard Worker             .map(|p| {
268*bb4ee6a4SAndroid Build Coastguard Worker                 let plane_offset = buffer_offset;
269*bb4ee6a4SAndroid Build Coastguard Worker                 buffer_offset += p.plane_size;
270*bb4ee6a4SAndroid Build Coastguard Worker 
271*bb4ee6a4SAndroid Build Coastguard Worker                 FramePlane {
272*bb4ee6a4SAndroid Build Coastguard Worker                     offset: plane_offset as usize,
273*bb4ee6a4SAndroid Build Coastguard Worker                     stride: p.stride as usize,
274*bb4ee6a4SAndroid Build Coastguard Worker                     size: p.plane_size as usize,
275*bb4ee6a4SAndroid Build Coastguard Worker                 }
276*bb4ee6a4SAndroid Build Coastguard Worker             })
277*bb4ee6a4SAndroid Build Coastguard Worker             .collect();
278*bb4ee6a4SAndroid Build Coastguard Worker 
279*bb4ee6a4SAndroid Build Coastguard Worker         Ok(GuestResource {
280*bb4ee6a4SAndroid Build Coastguard Worker             handle,
281*bb4ee6a4SAndroid Build Coastguard Worker             planes,
282*bb4ee6a4SAndroid Build Coastguard Worker             width: params.frame_width,
283*bb4ee6a4SAndroid Build Coastguard Worker             height: params.frame_height,
284*bb4ee6a4SAndroid Build Coastguard Worker             format: params.format.unwrap(),
285*bb4ee6a4SAndroid Build Coastguard Worker             guest_cpu_mappable: true,
286*bb4ee6a4SAndroid Build Coastguard Worker         })
287*bb4ee6a4SAndroid Build Coastguard Worker     }
288*bb4ee6a4SAndroid Build Coastguard Worker 
289*bb4ee6a4SAndroid Build Coastguard Worker     /// Try to convert an unresolved virtio object entry into a resolved object resource.
290*bb4ee6a4SAndroid Build Coastguard Worker     ///
291*bb4ee6a4SAndroid Build Coastguard Worker     /// Convert `object` into the object resource it represents and resolve it through `res_bridge`.
292*bb4ee6a4SAndroid Build Coastguard Worker     /// Returns an error if the object's UUID is invalid or cannot be resolved to a buffer object
293*bb4ee6a4SAndroid Build Coastguard Worker     /// by `res_bridge`.
from_virtio_object_entry( object: virtio_video_object_entry, res_bridge: &base::Tube, params: &Params, ) -> Result<GuestResource, ObjectResourceCreationError>294*bb4ee6a4SAndroid Build Coastguard Worker     pub fn from_virtio_object_entry(
295*bb4ee6a4SAndroid Build Coastguard Worker         object: virtio_video_object_entry,
296*bb4ee6a4SAndroid Build Coastguard Worker         res_bridge: &base::Tube,
297*bb4ee6a4SAndroid Build Coastguard Worker         params: &Params,
298*bb4ee6a4SAndroid Build Coastguard Worker     ) -> Result<GuestResource, ObjectResourceCreationError> {
299*bb4ee6a4SAndroid Build Coastguard Worker         // We trust that the caller has chosen the correct object type.
300*bb4ee6a4SAndroid Build Coastguard Worker         let uuid = u128::from_be_bytes(object.uuid);
301*bb4ee6a4SAndroid Build Coastguard Worker 
302*bb4ee6a4SAndroid Build Coastguard Worker         // TODO(stevensd): `Virtio3DBackend::resource_assign_uuid` is currently implemented to use
303*bb4ee6a4SAndroid Build Coastguard Worker         // 32-bits resource_handles as UUIDs. Once it starts using real UUIDs, we need to update
304*bb4ee6a4SAndroid Build Coastguard Worker         // this conversion.
305*bb4ee6a4SAndroid Build Coastguard Worker         let handle = TryInto::<u32>::try_into(uuid)
306*bb4ee6a4SAndroid Build Coastguard Worker             .map_err(|_| ObjectResourceCreationError::UuidNot32Bits(uuid))?;
307*bb4ee6a4SAndroid Build Coastguard Worker 
308*bb4ee6a4SAndroid Build Coastguard Worker         let buffer_info = match resource_bridge::get_resource_info(
309*bb4ee6a4SAndroid Build Coastguard Worker             res_bridge,
310*bb4ee6a4SAndroid Build Coastguard Worker             ResourceRequest::GetBuffer { id: handle },
311*bb4ee6a4SAndroid Build Coastguard Worker         ) {
312*bb4ee6a4SAndroid Build Coastguard Worker             Ok(ResourceInfo::Buffer(buffer_info)) => buffer_info,
313*bb4ee6a4SAndroid Build Coastguard Worker             Ok(_) => return Err(ObjectResourceCreationError::NotABuffer),
314*bb4ee6a4SAndroid Build Coastguard Worker             Err(e) => return Err(ObjectResourceCreationError::ResourceBridgeFailure(e)),
315*bb4ee6a4SAndroid Build Coastguard Worker         };
316*bb4ee6a4SAndroid Build Coastguard Worker 
317*bb4ee6a4SAndroid Build Coastguard Worker         let handle = GuestResourceHandle::VirtioObject(VirtioObjectHandle {
318*bb4ee6a4SAndroid Build Coastguard Worker             // SAFETY:
319*bb4ee6a4SAndroid Build Coastguard Worker             // Safe because `buffer_info.file` is a valid file descriptor and we are stealing
320*bb4ee6a4SAndroid Build Coastguard Worker             // it.
321*bb4ee6a4SAndroid Build Coastguard Worker             desc: unsafe {
322*bb4ee6a4SAndroid Build Coastguard Worker                 SafeDescriptor::from_raw_descriptor(buffer_info.handle.into_raw_descriptor())
323*bb4ee6a4SAndroid Build Coastguard Worker             },
324*bb4ee6a4SAndroid Build Coastguard Worker             modifier: buffer_info.modifier,
325*bb4ee6a4SAndroid Build Coastguard Worker         });
326*bb4ee6a4SAndroid Build Coastguard Worker 
327*bb4ee6a4SAndroid Build Coastguard Worker         // TODO(ishitatsuyuki): Right now, there are two sources of metadata: through the
328*bb4ee6a4SAndroid Build Coastguard Worker         //                      virtio_video_params fields, or through the buffer metadata provided
329*bb4ee6a4SAndroid Build Coastguard Worker         //                      by the VirtioObject backend.
330*bb4ee6a4SAndroid Build Coastguard Worker         //                      Unfortunately neither is sufficient. The virtio_video_params struct
331*bb4ee6a4SAndroid Build Coastguard Worker         //                      lacks the plane offset, while some virtio-gpu backend doesn't
332*bb4ee6a4SAndroid Build Coastguard Worker         //                      have information about the plane size, or in some cases even the
333*bb4ee6a4SAndroid Build Coastguard Worker         //                      overall frame width and height.
334*bb4ee6a4SAndroid Build Coastguard Worker         //                      We will mix-and-match metadata from the more reliable data source
335*bb4ee6a4SAndroid Build Coastguard Worker         //                      below; ideally this should be fixed to use single source of truth.
336*bb4ee6a4SAndroid Build Coastguard Worker         let planes = params
337*bb4ee6a4SAndroid Build Coastguard Worker             .plane_formats
338*bb4ee6a4SAndroid Build Coastguard Worker             .iter()
339*bb4ee6a4SAndroid Build Coastguard Worker             .zip(&buffer_info.planes)
340*bb4ee6a4SAndroid Build Coastguard Worker             .map(|(param, buffer)| FramePlane {
341*bb4ee6a4SAndroid Build Coastguard Worker                 // When the virtio object backend was implemented, the buffer and stride was sourced
342*bb4ee6a4SAndroid Build Coastguard Worker                 // from the object backend's metadata (`buffer`). To lean on the safe side, we'll
343*bb4ee6a4SAndroid Build Coastguard Worker                 // keep using data from `buffer`, even in case of stride it's also provided by
344*bb4ee6a4SAndroid Build Coastguard Worker                 // `param`.
345*bb4ee6a4SAndroid Build Coastguard Worker                 offset: buffer.offset as usize,
346*bb4ee6a4SAndroid Build Coastguard Worker                 stride: buffer.stride as usize,
347*bb4ee6a4SAndroid Build Coastguard Worker                 size: param.plane_size as usize,
348*bb4ee6a4SAndroid Build Coastguard Worker             })
349*bb4ee6a4SAndroid Build Coastguard Worker             .collect();
350*bb4ee6a4SAndroid Build Coastguard Worker 
351*bb4ee6a4SAndroid Build Coastguard Worker         Ok(GuestResource {
352*bb4ee6a4SAndroid Build Coastguard Worker             handle,
353*bb4ee6a4SAndroid Build Coastguard Worker             planes,
354*bb4ee6a4SAndroid Build Coastguard Worker             width: params.frame_width,
355*bb4ee6a4SAndroid Build Coastguard Worker             height: params.frame_height,
356*bb4ee6a4SAndroid Build Coastguard Worker             format: params.format.unwrap(),
357*bb4ee6a4SAndroid Build Coastguard Worker             guest_cpu_mappable: buffer_info.guest_cpu_mappable,
358*bb4ee6a4SAndroid Build Coastguard Worker         })
359*bb4ee6a4SAndroid Build Coastguard Worker     }
360*bb4ee6a4SAndroid Build Coastguard Worker 
361*bb4ee6a4SAndroid Build Coastguard Worker     #[cfg(feature = "video-encoder")]
try_clone(&self) -> Result<Self, base::Error>362*bb4ee6a4SAndroid Build Coastguard Worker     pub fn try_clone(&self) -> Result<Self, base::Error> {
363*bb4ee6a4SAndroid Build Coastguard Worker         Ok(Self {
364*bb4ee6a4SAndroid Build Coastguard Worker             handle: self.handle.try_clone()?,
365*bb4ee6a4SAndroid Build Coastguard Worker             planes: self.planes.clone(),
366*bb4ee6a4SAndroid Build Coastguard Worker             width: self.width,
367*bb4ee6a4SAndroid Build Coastguard Worker             height: self.height,
368*bb4ee6a4SAndroid Build Coastguard Worker             format: self.format,
369*bb4ee6a4SAndroid Build Coastguard Worker             guest_cpu_mappable: self.guest_cpu_mappable,
370*bb4ee6a4SAndroid Build Coastguard Worker         })
371*bb4ee6a4SAndroid Build Coastguard Worker     }
372*bb4ee6a4SAndroid Build Coastguard Worker }
373*bb4ee6a4SAndroid Build Coastguard Worker 
374*bb4ee6a4SAndroid Build Coastguard Worker #[cfg(test)]
375*bb4ee6a4SAndroid Build Coastguard Worker mod tests {
376*bb4ee6a4SAndroid Build Coastguard Worker     use base::MappedRegion;
377*bb4ee6a4SAndroid Build Coastguard Worker     use base::SharedMemory;
378*bb4ee6a4SAndroid Build Coastguard Worker 
379*bb4ee6a4SAndroid Build Coastguard Worker     use super::*;
380*bb4ee6a4SAndroid Build Coastguard Worker 
381*bb4ee6a4SAndroid Build Coastguard Worker     /// Creates a sparse guest memory handle using as many pages as there are entries in
382*bb4ee6a4SAndroid Build Coastguard Worker     /// `page_order`. The page with index `0` will be the first page, `1` will be the second page,
383*bb4ee6a4SAndroid Build Coastguard Worker     /// etc.
384*bb4ee6a4SAndroid Build Coastguard Worker     ///
385*bb4ee6a4SAndroid Build Coastguard Worker     /// The memory handle is filled with increasing u32s starting from page 0, then page 1, and so
386*bb4ee6a4SAndroid Build Coastguard Worker     /// on. Finally the handle is mapped into a linear space and we check that the written integers
387*bb4ee6a4SAndroid Build Coastguard Worker     /// appear in the expected order.
check_guest_mem_handle(page_order: &[usize])388*bb4ee6a4SAndroid Build Coastguard Worker     fn check_guest_mem_handle(page_order: &[usize]) {
389*bb4ee6a4SAndroid Build Coastguard Worker         const PAGE_SIZE: usize = 0x1000;
390*bb4ee6a4SAndroid Build Coastguard Worker         const U32_SIZE: usize = std::mem::size_of::<u32>();
391*bb4ee6a4SAndroid Build Coastguard Worker         const ENTRIES_PER_PAGE: usize = PAGE_SIZE / std::mem::size_of::<u32>();
392*bb4ee6a4SAndroid Build Coastguard Worker 
393*bb4ee6a4SAndroid Build Coastguard Worker         // Fill a vector of the same size as the handle with u32s of increasing value, following
394*bb4ee6a4SAndroid Build Coastguard Worker         // the page layout given as argument.
395*bb4ee6a4SAndroid Build Coastguard Worker         let mut data = vec![0u8; PAGE_SIZE * page_order.len()];
396*bb4ee6a4SAndroid Build Coastguard Worker         for (page_index, page) in page_order.iter().enumerate() {
397*bb4ee6a4SAndroid Build Coastguard Worker             let page_slice = &mut data[(page * PAGE_SIZE)..((page + 1) * PAGE_SIZE)];
398*bb4ee6a4SAndroid Build Coastguard Worker             for (index, chunk) in page_slice.chunks_exact_mut(4).enumerate() {
399*bb4ee6a4SAndroid Build Coastguard Worker                 let sized_chunk: &mut [u8; 4] = chunk.try_into().unwrap();
400*bb4ee6a4SAndroid Build Coastguard Worker                 *sized_chunk = (((page_index * ENTRIES_PER_PAGE) + index) as u32).to_ne_bytes();
401*bb4ee6a4SAndroid Build Coastguard Worker             }
402*bb4ee6a4SAndroid Build Coastguard Worker         }
403*bb4ee6a4SAndroid Build Coastguard Worker 
404*bb4ee6a4SAndroid Build Coastguard Worker         // Copy the initialized vector's content into an anonymous shared memory.
405*bb4ee6a4SAndroid Build Coastguard Worker         let mem = SharedMemory::new("data-dest", data.len() as u64).unwrap();
406*bb4ee6a4SAndroid Build Coastguard Worker         let mapping = MemoryMappingBuilder::new(mem.size() as usize)
407*bb4ee6a4SAndroid Build Coastguard Worker             .from_shared_memory(&mem)
408*bb4ee6a4SAndroid Build Coastguard Worker             .build()
409*bb4ee6a4SAndroid Build Coastguard Worker             .unwrap();
410*bb4ee6a4SAndroid Build Coastguard Worker         assert_eq!(mapping.write_slice(&data, 0).unwrap(), data.len());
411*bb4ee6a4SAndroid Build Coastguard Worker 
412*bb4ee6a4SAndroid Build Coastguard Worker         // Create the `GuestMemHandle` we will try to map and retrieve the data from.
413*bb4ee6a4SAndroid Build Coastguard Worker         let mem_handle = GuestResourceHandle::GuestPages(GuestMemHandle {
414*bb4ee6a4SAndroid Build Coastguard Worker             desc: base::clone_descriptor(&mem).unwrap(),
415*bb4ee6a4SAndroid Build Coastguard Worker             mem_areas: page_order
416*bb4ee6a4SAndroid Build Coastguard Worker                 .iter()
417*bb4ee6a4SAndroid Build Coastguard Worker                 .map(|&page| GuestMemArea {
418*bb4ee6a4SAndroid Build Coastguard Worker                     offset: page as u64 * PAGE_SIZE as u64,
419*bb4ee6a4SAndroid Build Coastguard Worker                     length: PAGE_SIZE,
420*bb4ee6a4SAndroid Build Coastguard Worker                 })
421*bb4ee6a4SAndroid Build Coastguard Worker                 .collect(),
422*bb4ee6a4SAndroid Build Coastguard Worker         });
423*bb4ee6a4SAndroid Build Coastguard Worker 
424*bb4ee6a4SAndroid Build Coastguard Worker         // Map the handle into a linear memory area, retrieve its data into a new vector, and check
425*bb4ee6a4SAndroid Build Coastguard Worker         // that its u32s appear to increase linearly.
426*bb4ee6a4SAndroid Build Coastguard Worker         let mapping = mem_handle.get_mapping(0, mem.size() as usize).unwrap();
427*bb4ee6a4SAndroid Build Coastguard Worker         let mut data = vec![0u8; PAGE_SIZE * page_order.len()];
428*bb4ee6a4SAndroid Build Coastguard Worker         // SAFETY: src and dst are valid and aligned
429*bb4ee6a4SAndroid Build Coastguard Worker         unsafe { std::ptr::copy_nonoverlapping(mapping.as_ptr(), data.as_mut_ptr(), data.len()) };
430*bb4ee6a4SAndroid Build Coastguard Worker         for (index, chunk) in data.chunks_exact(U32_SIZE).enumerate() {
431*bb4ee6a4SAndroid Build Coastguard Worker             let sized_chunk: &[u8; 4] = chunk.try_into().unwrap();
432*bb4ee6a4SAndroid Build Coastguard Worker             assert_eq!(u32::from_ne_bytes(*sized_chunk), index as u32);
433*bb4ee6a4SAndroid Build Coastguard Worker         }
434*bb4ee6a4SAndroid Build Coastguard Worker     }
435*bb4ee6a4SAndroid Build Coastguard Worker 
436*bb4ee6a4SAndroid Build Coastguard Worker     // Fill a guest memory handle with a single memory page.
437*bb4ee6a4SAndroid Build Coastguard Worker     // Then check that the data can be properly mapped and appears in the expected order.
438*bb4ee6a4SAndroid Build Coastguard Worker     #[test]
test_single_guest_mem_handle()439*bb4ee6a4SAndroid Build Coastguard Worker     fn test_single_guest_mem_handle() {
440*bb4ee6a4SAndroid Build Coastguard Worker         check_guest_mem_handle(&[0])
441*bb4ee6a4SAndroid Build Coastguard Worker     }
442*bb4ee6a4SAndroid Build Coastguard Worker 
443*bb4ee6a4SAndroid Build Coastguard Worker     // Fill a guest memory handle with 4 memory pages that are contiguous.
444*bb4ee6a4SAndroid Build Coastguard Worker     // Then check that the pages appear in the expected order in the mapping.
445*bb4ee6a4SAndroid Build Coastguard Worker     #[test]
test_linear_guest_mem_handle()446*bb4ee6a4SAndroid Build Coastguard Worker     fn test_linear_guest_mem_handle() {
447*bb4ee6a4SAndroid Build Coastguard Worker         check_guest_mem_handle(&[0, 1, 2, 3])
448*bb4ee6a4SAndroid Build Coastguard Worker     }
449*bb4ee6a4SAndroid Build Coastguard Worker 
450*bb4ee6a4SAndroid Build Coastguard Worker     // Fill a guest memory handle with 8 pages mapped in non-linear order.
451*bb4ee6a4SAndroid Build Coastguard Worker     // Then check that the pages appear in the expected order in the mapping.
452*bb4ee6a4SAndroid Build Coastguard Worker     #[test]
test_sparse_guest_mem_handle()453*bb4ee6a4SAndroid Build Coastguard Worker     fn test_sparse_guest_mem_handle() {
454*bb4ee6a4SAndroid Build Coastguard Worker         check_guest_mem_handle(&[1, 7, 6, 3, 5, 0, 4, 2])
455*bb4ee6a4SAndroid Build Coastguard Worker     }
456*bb4ee6a4SAndroid Build Coastguard Worker }
457