• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 // Copyright 2020 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 //! rutabaga_core: Cross-platform, Rust-based, Wayland and Vulkan centric GPU virtualization.
6 use std::collections::BTreeMap as Map;
7 use std::convert::TryInto;
8 use std::io::IoSliceMut;
9 use std::io::Read;
10 use std::io::Write;
11 use std::sync::Arc;
12 
13 use crate::cross_domain::CrossDomain;
14 #[cfg(feature = "gfxstream")]
15 use crate::gfxstream::Gfxstream;
16 use crate::rutabaga_2d::Rutabaga2D;
17 use crate::rutabaga_os::MemoryMapping;
18 use crate::rutabaga_os::SafeDescriptor;
19 use crate::rutabaga_snapshot::RutabagaResourceSnapshot;
20 use crate::rutabaga_snapshot::RutabagaSnapshot;
21 use crate::rutabaga_utils::*;
22 #[cfg(feature = "virgl_renderer")]
23 use crate::virgl_renderer::VirglRenderer;
24 
25 const RUTABAGA_DEFAULT_WIDTH: u32 = 1280;
26 const RUTABAGA_DEFAULT_HEIGHT: u32 = 1024;
27 
28 /// Information required for 2D functionality.
29 pub struct Rutabaga2DInfo {
30     pub width: u32,
31     pub height: u32,
32     pub host_mem: Vec<u8>,
33 }
34 
35 /// A Rutabaga resource, supporting 2D and 3D rutabaga features.  Assumes a single-threaded library.
36 pub struct RutabagaResource {
37     pub resource_id: u32,
38     pub handle: Option<Arc<RutabagaHandle>>,
39     pub blob: bool,
40     pub blob_mem: u32,
41     pub blob_flags: u32,
42     pub map_info: Option<u32>,
43     pub info_2d: Option<Rutabaga2DInfo>,
44     pub info_3d: Option<Resource3DInfo>,
45     pub vulkan_info: Option<VulkanInfo>,
46     pub backing_iovecs: Option<Vec<RutabagaIovec>>,
47 
48     /// Bitmask of components that have already imported this resource
49     pub component_mask: u8,
50     pub size: u64,
51     pub mapping: Option<MemoryMapping>,
52 }
53 
54 /// A RutabagaComponent is a building block of the Virtual Graphics Interface (VGI).  Each component
55 /// on it's own is sufficient to virtualize graphics on many Google products.  These components wrap
56 /// libraries like gfxstream or virglrenderer, and Rutabaga's own 2D and cross-domain prototype
57 /// functionality.
58 ///
59 /// Most methods return a `RutabagaResult` that indicate the success, failure, or requested data for
60 /// the given command.
61 pub trait RutabagaComponent {
62     /// Implementations should return the version and size of the given capset_id.  (0, 0) is
63     /// returned by default.
get_capset_info(&self, _capset_id: u32) -> (u32, u32)64     fn get_capset_info(&self, _capset_id: u32) -> (u32, u32) {
65         (0, 0)
66     }
67 
68     /// Implementations should return the capabilites of given a `capset_id` and `version`.  A
69     /// zero-sized array is returned by default.
get_capset(&self, _capset_id: u32, _version: u32) -> Vec<u8>70     fn get_capset(&self, _capset_id: u32, _version: u32) -> Vec<u8> {
71         Vec::new()
72     }
73 
74     /// Implementations should set their internal context to be the reserved context 0.
force_ctx_0(&self)75     fn force_ctx_0(&self) {}
76 
77     /// Implementations must create a fence that represents the completion of prior work.  This is
78     /// required for synchronization with the guest kernel.
create_fence(&mut self, _fence: RutabagaFence) -> RutabagaResult<()>79     fn create_fence(&mut self, _fence: RutabagaFence) -> RutabagaResult<()> {
80         Ok(())
81     }
82 
83     /// Used only by VirglRenderer to poll when its poll_descriptor is signaled.
event_poll(&self)84     fn event_poll(&self) {}
85 
86     /// Used only by VirglRenderer to return a poll_descriptor that is signaled when a poll() is
87     /// necessary.
poll_descriptor(&self) -> Option<SafeDescriptor>88     fn poll_descriptor(&self) -> Option<SafeDescriptor> {
89         None
90     }
91 
92     /// Implementations must create a resource with the given metadata.  For 2D rutabaga components,
93     /// this a system memory allocation.  For 3D components, this is typically a GL texture or
94     /// buffer.  Vulkan components should use blob resources instead.
create_3d( &self, resource_id: u32, _resource_create_3d: ResourceCreate3D, ) -> RutabagaResult<RutabagaResource>95     fn create_3d(
96         &self,
97         resource_id: u32,
98         _resource_create_3d: ResourceCreate3D,
99     ) -> RutabagaResult<RutabagaResource> {
100         Ok(RutabagaResource {
101             resource_id,
102             handle: None,
103             blob: false,
104             blob_mem: 0,
105             blob_flags: 0,
106             map_info: None,
107             info_2d: None,
108             info_3d: None,
109             vulkan_info: None,
110             backing_iovecs: None,
111             component_mask: 0,
112             size: 0,
113             mapping: None,
114         })
115     }
116 
117     /// Implementations must attach `vecs` to the resource.
attach_backing( &self, _resource_id: u32, _vecs: &mut Vec<RutabagaIovec>, ) -> RutabagaResult<()>118     fn attach_backing(
119         &self,
120         _resource_id: u32,
121         _vecs: &mut Vec<RutabagaIovec>,
122     ) -> RutabagaResult<()> {
123         Ok(())
124     }
125 
126     /// Implementations must detach `vecs` from the resource.
detach_backing(&self, _resource_id: u32)127     fn detach_backing(&self, _resource_id: u32) {}
128 
129     /// Implementations must release the guest kernel reference on the resource.
unref_resource(&self, _resource_id: u32)130     fn unref_resource(&self, _resource_id: u32) {}
131 
132     /// Implementations must perform the transfer write operation.  For 2D rutabaga components, this
133     /// done via memcpy().  For 3D components, this is typically done via glTexSubImage(..).
transfer_write( &self, _ctx_id: u32, _resource: &mut RutabagaResource, _transfer: Transfer3D, ) -> RutabagaResult<()>134     fn transfer_write(
135         &self,
136         _ctx_id: u32,
137         _resource: &mut RutabagaResource,
138         _transfer: Transfer3D,
139     ) -> RutabagaResult<()> {
140         Ok(())
141     }
142 
143     /// Implementations must perform the transfer read operation.  For 2D rutabaga components, this
144     /// done via memcpy().  For 3D components, this is typically done via glReadPixels(..).
transfer_read( &self, _ctx_id: u32, _resource: &mut RutabagaResource, _transfer: Transfer3D, _buf: Option<IoSliceMut>, ) -> RutabagaResult<()>145     fn transfer_read(
146         &self,
147         _ctx_id: u32,
148         _resource: &mut RutabagaResource,
149         _transfer: Transfer3D,
150         _buf: Option<IoSliceMut>,
151     ) -> RutabagaResult<()> {
152         Ok(())
153     }
154 
155     /// Implementations must flush the given resource to the display.
resource_flush(&self, _resource_id: &mut RutabagaResource) -> RutabagaResult<()>156     fn resource_flush(&self, _resource_id: &mut RutabagaResource) -> RutabagaResult<()> {
157         Err(RutabagaError::Unsupported)
158     }
159 
160     /// Implementations must create a blob resource on success.  The memory parameters, size, and
161     /// usage of the blob resource is given by `resource_create_blob`.
create_blob( &mut self, _ctx_id: u32, _resource_id: u32, _resource_create_blob: ResourceCreateBlob, _iovec_opt: Option<Vec<RutabagaIovec>>, _handle_opt: Option<RutabagaHandle>, ) -> RutabagaResult<RutabagaResource>162     fn create_blob(
163         &mut self,
164         _ctx_id: u32,
165         _resource_id: u32,
166         _resource_create_blob: ResourceCreateBlob,
167         _iovec_opt: Option<Vec<RutabagaIovec>>,
168         _handle_opt: Option<RutabagaHandle>,
169     ) -> RutabagaResult<RutabagaResource> {
170         Err(RutabagaError::Unsupported)
171     }
172 
173     /// Implementations must map the blob resource on success.  This is typically done by
174     /// glMapBufferRange(...) or vkMapMemory.
map(&self, _resource_id: u32) -> RutabagaResult<RutabagaMapping>175     fn map(&self, _resource_id: u32) -> RutabagaResult<RutabagaMapping> {
176         Err(RutabagaError::Unsupported)
177     }
178 
179     /// Implementations must unmap the blob resource on success.  This is typically done by
180     /// glUnmapBuffer(...) or vkUnmapMemory.
unmap(&self, _resource_id: u32) -> RutabagaResult<()>181     fn unmap(&self, _resource_id: u32) -> RutabagaResult<()> {
182         Err(RutabagaError::Unsupported)
183     }
184 
185     /// Implementations must return a RutabagaHandle of the fence on success.
export_fence(&self, _fence_id: u64) -> RutabagaResult<RutabagaHandle>186     fn export_fence(&self, _fence_id: u64) -> RutabagaResult<RutabagaHandle> {
187         Err(RutabagaError::Unsupported)
188     }
189 
190     /// Implementations must create a context for submitting commands.  The command stream of the
191     /// context is determined by `context_init`.  For virgl contexts, it is a Gallium/TGSI command
192     /// stream.  For gfxstream contexts, it's an autogenerated Vulkan or GLES streams.
create_context( &self, _ctx_id: u32, _context_init: u32, _context_name: Option<&str>, _fence_handler: RutabagaFenceHandler, ) -> RutabagaResult<Box<dyn RutabagaContext>>193     fn create_context(
194         &self,
195         _ctx_id: u32,
196         _context_init: u32,
197         _context_name: Option<&str>,
198         _fence_handler: RutabagaFenceHandler,
199     ) -> RutabagaResult<Box<dyn RutabagaContext>> {
200         Err(RutabagaError::Unsupported)
201     }
202 
203     /// Implementations must snapshot to the specified directory
snapshot(&self, _directory: &str) -> RutabagaResult<()>204     fn snapshot(&self, _directory: &str) -> RutabagaResult<()> {
205         Err(RutabagaError::Unsupported)
206     }
207 
208     /// Implementations must restore from the specified directory
restore(&self, _directory: &str) -> RutabagaResult<()>209     fn restore(&self, _directory: &str) -> RutabagaResult<()> {
210         Err(RutabagaError::Unsupported)
211     }
212 }
213 
214 pub trait RutabagaContext {
215     /// Implementations must return a RutabagaResource given the `resource_create_blob` parameters.
context_create_blob( &mut self, _resource_id: u32, _resource_create_blob: ResourceCreateBlob, _handle_opt: Option<RutabagaHandle>, ) -> RutabagaResult<RutabagaResource>216     fn context_create_blob(
217         &mut self,
218         _resource_id: u32,
219         _resource_create_blob: ResourceCreateBlob,
220         _handle_opt: Option<RutabagaHandle>,
221     ) -> RutabagaResult<RutabagaResource> {
222         Err(RutabagaError::Unsupported)
223     }
224 
225     /// Implementations must handle the context-specific command stream.
submit_cmd(&mut self, _commands: &mut [u8], _fence_ids: &[u64]) -> RutabagaResult<()>226     fn submit_cmd(&mut self, _commands: &mut [u8], _fence_ids: &[u64]) -> RutabagaResult<()>;
227 
228     /// Implementations may use `resource` in this context's command stream.
attach(&mut self, _resource: &mut RutabagaResource)229     fn attach(&mut self, _resource: &mut RutabagaResource);
230 
231     /// Implementations must stop using `resource` in this context's command stream.
detach(&mut self, _resource: &RutabagaResource)232     fn detach(&mut self, _resource: &RutabagaResource);
233 
234     /// Implementations must create a fence on specified `ring_idx` in `fence`.  This
235     /// allows for multiple synchronizations timelines per RutabagaContext.
context_create_fence(&mut self, _fence: RutabagaFence) -> RutabagaResult<()>236     fn context_create_fence(&mut self, _fence: RutabagaFence) -> RutabagaResult<()> {
237         Err(RutabagaError::Unsupported)
238     }
239 
240     /// Implementations must return the component type associated with the context.
component_type(&self) -> RutabagaComponentType241     fn component_type(&self) -> RutabagaComponentType;
242 }
243 
244 #[derive(Copy, Clone)]
245 struct RutabagaCapsetInfo {
246     pub capset_id: u32,
247     pub component: RutabagaComponentType,
248     pub name: &'static str,
249 }
250 
251 const RUTABAGA_CAPSETS: [RutabagaCapsetInfo; 9] = [
252     RutabagaCapsetInfo {
253         capset_id: RUTABAGA_CAPSET_VIRGL,
254         component: RutabagaComponentType::VirglRenderer,
255         name: "virgl",
256     },
257     RutabagaCapsetInfo {
258         capset_id: RUTABAGA_CAPSET_VIRGL2,
259         component: RutabagaComponentType::VirglRenderer,
260         name: "virgl2",
261     },
262     RutabagaCapsetInfo {
263         capset_id: RUTABAGA_CAPSET_GFXSTREAM_VULKAN,
264         component: RutabagaComponentType::Gfxstream,
265         name: "gfxstream-vulkan",
266     },
267     RutabagaCapsetInfo {
268         capset_id: RUTABAGA_CAPSET_VENUS,
269         component: RutabagaComponentType::VirglRenderer,
270         name: "venus",
271     },
272     RutabagaCapsetInfo {
273         capset_id: RUTABAGA_CAPSET_CROSS_DOMAIN,
274         component: RutabagaComponentType::CrossDomain,
275         name: "cross-domain",
276     },
277     RutabagaCapsetInfo {
278         capset_id: RUTABAGA_CAPSET_DRM,
279         component: RutabagaComponentType::VirglRenderer,
280         name: "drm",
281     },
282     RutabagaCapsetInfo {
283         capset_id: RUTABAGA_CAPSET_GFXSTREAM_MAGMA,
284         component: RutabagaComponentType::Gfxstream,
285         name: "gfxstream-magma",
286     },
287     RutabagaCapsetInfo {
288         capset_id: RUTABAGA_CAPSET_GFXSTREAM_GLES,
289         component: RutabagaComponentType::Gfxstream,
290         name: "gfxstream-gles",
291     },
292     RutabagaCapsetInfo {
293         capset_id: RUTABAGA_CAPSET_GFXSTREAM_COMPOSER,
294         component: RutabagaComponentType::Gfxstream,
295         name: "gfxstream-composer",
296     },
297 ];
298 
calculate_capset_mask<'a, I: Iterator<Item = &'a str>>(context_names: I) -> u64299 pub fn calculate_capset_mask<'a, I: Iterator<Item = &'a str>>(context_names: I) -> u64 {
300     let mut capset_mask = 0;
301     for name in context_names {
302         if let Some(capset) = RUTABAGA_CAPSETS.iter().find(|capset| capset.name == name) {
303             capset_mask |= 1 << capset.capset_id;
304         };
305     }
306 
307     capset_mask
308 }
309 
calculate_capset_names(capset_mask: u64) -> Vec<String>310 pub fn calculate_capset_names(capset_mask: u64) -> Vec<String> {
311     RUTABAGA_CAPSETS
312         .iter()
313         .filter(|capset| capset_mask & (1 << capset.capset_id) != 0)
314         .map(|capset| capset.name.to_string())
315         .collect()
316 }
317 
calculate_component(component_mask: u8) -> RutabagaResult<RutabagaComponentType>318 fn calculate_component(component_mask: u8) -> RutabagaResult<RutabagaComponentType> {
319     if component_mask.count_ones() != 1 {
320         return Err(RutabagaError::SpecViolation("can't infer single component"));
321     }
322 
323     match component_mask.trailing_zeros() {
324         0 => Ok(RutabagaComponentType::Rutabaga2D),
325         1 => Ok(RutabagaComponentType::VirglRenderer),
326         2 => Ok(RutabagaComponentType::Gfxstream),
327         3 => Ok(RutabagaComponentType::CrossDomain),
328         _ => Err(RutabagaError::InvalidComponent),
329     }
330 }
331 
332 /// The global libary handle used to query capability sets, create resources and contexts.
333 ///
334 /// Currently, Rutabaga only supports one default component.  Many components running at the
335 /// same time is a stretch goal of Rutabaga GFX.
336 ///
337 /// Not thread-safe, but can be made so easily.  Making non-Rutabaga, C/C++ components
338 /// thread-safe is more difficult.
339 pub struct Rutabaga {
340     resources: Map<u32, RutabagaResource>,
341     contexts: Map<u32, Box<dyn RutabagaContext>>,
342     // Declare components after resources and contexts such that it is dropped last.
343     components: Map<RutabagaComponentType, Box<dyn RutabagaComponent>>,
344     default_component: RutabagaComponentType,
345     capset_info: Vec<RutabagaCapsetInfo>,
346     fence_handler: RutabagaFenceHandler,
347 }
348 
349 impl Rutabaga {
350     /// Take a snapshot of Rutabaga's current state. The snapshot is serialized into an opaque byte
351     /// stream and written to `w`.
snapshot(&self, w: &mut impl Write, directory: &str) -> RutabagaResult<()>352     pub fn snapshot(&self, w: &mut impl Write, directory: &str) -> RutabagaResult<()> {
353         if self.default_component == RutabagaComponentType::Gfxstream {
354             let component = self
355                 .components
356                 .get(&self.default_component)
357                 .ok_or(RutabagaError::InvalidComponent)?;
358 
359             component.snapshot(directory)
360         } else if self.default_component == RutabagaComponentType::Rutabaga2D {
361             let snapshot = RutabagaSnapshot {
362                 resources: self
363                     .resources
364                     .iter()
365                     .map(|(i, r)| {
366                         let info = r.info_2d.as_ref().ok_or(RutabagaError::Unsupported)?;
367                         assert_eq!(
368                             usize::try_from(info.width * info.height * 4).unwrap(),
369                             info.host_mem.len()
370                         );
371                         assert_eq!(usize::try_from(r.size).unwrap(), info.host_mem.len());
372                         let s = RutabagaResourceSnapshot {
373                             resource_id: r.resource_id,
374                             width: info.width,
375                             height: info.height,
376                         };
377                         Ok((*i, s))
378                     })
379                     .collect::<RutabagaResult<_>>()?,
380             };
381 
382             return snapshot.serialize_to(w).map_err(RutabagaError::IoError);
383         } else {
384             Err(RutabagaError::Unsupported)
385         }
386     }
387 
388     /// Restore Rutabaga to a previously snapshot'd state.
389     ///
390     /// Snapshotting on one host machine and then restoring on another ("host migration") might
391     /// work for very similar machines but isn't explicitly supported yet.
392     ///
393     /// Rutabaga will recreate resources internally, but it's the VMM's responsibility to re-attach
394     /// backing iovecs and re-map the memory after re-creation. Specifically:
395     ///
396     /// * Mode2D
397     ///    * The VMM must call `Rutabaga::attach_backing` calls for all resources that had backing
398     ///      memory at the time of the snapshot.
399     /// * ModeVirglRenderer
400     ///    * Not supported.
401     /// * ModeGfxstream
402     ///    * WiP support.
403     ///
404     /// NOTES: This is required because the pointers to backing memory aren't stable, help from the
405     /// VMM is necessary. In an alternative approach, the VMM could supply Rutabaga with callbacks
406     /// to translate to/from stable guest physical addresses, but it is unclear how well that
407     /// approach would scale to support 3D modes, which have others problems that require VMM help,
408     /// like resource handles.
restore(&mut self, r: &mut impl Read, directory: &str) -> RutabagaResult<()>409     pub fn restore(&mut self, r: &mut impl Read, directory: &str) -> RutabagaResult<()> {
410         if self.default_component == RutabagaComponentType::Gfxstream {
411             let component = self
412                 .components
413                 .get_mut(&self.default_component)
414                 .ok_or(RutabagaError::InvalidComponent)?;
415 
416             component.restore(directory)
417         } else if self.default_component == RutabagaComponentType::Rutabaga2D {
418             let snapshot = RutabagaSnapshot::deserialize_from(r).map_err(RutabagaError::IoError)?;
419 
420             self.resources = snapshot
421                 .resources
422                 .into_iter()
423                 .map(|(i, s)| {
424                     let size = u64::from(s.width * s.height * 4);
425                     let r = RutabagaResource {
426                         resource_id: s.resource_id,
427                         handle: None,
428                         blob: false,
429                         blob_mem: 0,
430                         blob_flags: 0,
431                         map_info: None,
432                         info_2d: Some(Rutabaga2DInfo {
433                             width: s.width,
434                             height: s.height,
435                             host_mem: vec![0; usize::try_from(size).unwrap()],
436                         }),
437                         info_3d: None,
438                         vulkan_info: None,
439                         // NOTE: `RutabagaResource::backing_iovecs` isn't snapshotted because the
440                         // pointers won't be valid at restore time, see the `Rutabaga::restore` doc.
441                         // If the client doesn't attach new iovecs, the restored resource will
442                         // behave as if they had been detached (instead of segfaulting on the stale
443                         // iovec pointers).
444                         backing_iovecs: None,
445                         component_mask: 1 << (RutabagaComponentType::Rutabaga2D as u8),
446                         size,
447                         mapping: None,
448                     };
449                     (i, r)
450                 })
451                 .collect();
452 
453             return Ok(());
454         } else {
455             Err(RutabagaError::Unsupported)
456         }
457     }
458 
capset_id_to_component_type(&self, capset_id: u32) -> RutabagaResult<RutabagaComponentType>459     fn capset_id_to_component_type(&self, capset_id: u32) -> RutabagaResult<RutabagaComponentType> {
460         let component = self
461             .capset_info
462             .iter()
463             .find(|capset_info| capset_info.capset_id == capset_id)
464             .ok_or(RutabagaError::InvalidCapset)?
465             .component;
466 
467         Ok(component)
468     }
469 
capset_index_to_component_info(&self, index: u32) -> RutabagaResult<RutabagaCapsetInfo>470     fn capset_index_to_component_info(&self, index: u32) -> RutabagaResult<RutabagaCapsetInfo> {
471         let idx = index as usize;
472         if idx >= self.capset_info.len() {
473             return Err(RutabagaError::InvalidCapset);
474         }
475 
476         Ok(self.capset_info[idx])
477     }
478 
479     /// Gets the version and size for the capabilty set `index`.
get_capset_info(&self, index: u32) -> RutabagaResult<(u32, u32, u32)>480     pub fn get_capset_info(&self, index: u32) -> RutabagaResult<(u32, u32, u32)> {
481         let capset_info = self.capset_index_to_component_info(index)?;
482 
483         let component = self
484             .components
485             .get(&capset_info.component)
486             .ok_or(RutabagaError::InvalidComponent)?;
487 
488         let (capset_version, capset_size) = component.get_capset_info(capset_info.capset_id);
489         Ok((capset_info.capset_id, capset_version, capset_size))
490     }
491 
492     /// Gets the capability set for the `capset_id` and `version`.
493     /// Each capability set is associated with a context type, which is associated
494     /// with a rutabaga component.
get_capset(&self, capset_id: u32, version: u32) -> RutabagaResult<Vec<u8>>495     pub fn get_capset(&self, capset_id: u32, version: u32) -> RutabagaResult<Vec<u8>> {
496         // The default workaround is just until context types are fully supported in all
497         // Google kernels.
498         let component_type = self
499             .capset_id_to_component_type(capset_id)
500             .unwrap_or(self.default_component);
501 
502         let component = self
503             .components
504             .get(&component_type)
505             .ok_or(RutabagaError::InvalidComponent)?;
506 
507         Ok(component.get_capset(capset_id, version))
508     }
509 
510     /// Gets the number of capsets
get_num_capsets(&self) -> u32511     pub fn get_num_capsets(&self) -> u32 {
512         self.capset_info.len() as u32
513     }
514 
515     /// Forces context zero for the default rutabaga component.
force_ctx_0(&self)516     pub fn force_ctx_0(&self) {
517         if let Some(component) = self.components.get(&self.default_component) {
518             component.force_ctx_0();
519         }
520     }
521 
522     /// Creates a fence with the given `fence`.
523     /// If the flags include RUTABAGA_FLAG_INFO_RING_IDX, then the fence is created on a
524     /// specific timeline on the specific context.
create_fence(&mut self, fence: RutabagaFence) -> RutabagaResult<()>525     pub fn create_fence(&mut self, fence: RutabagaFence) -> RutabagaResult<()> {
526         if fence.flags & RUTABAGA_FLAG_INFO_RING_IDX != 0 {
527             let ctx = self
528                 .contexts
529                 .get_mut(&fence.ctx_id)
530                 .ok_or(RutabagaError::InvalidContextId)?;
531 
532             ctx.context_create_fence(fence)?;
533         } else {
534             let component = self
535                 .components
536                 .get_mut(&self.default_component)
537                 .ok_or(RutabagaError::InvalidComponent)?;
538 
539             component.create_fence(fence)?;
540         }
541 
542         Ok(())
543     }
544 
545     /// Polls the default rutabaga component.
event_poll(&self)546     pub fn event_poll(&self) {
547         if let Some(component) = self.components.get(&self.default_component) {
548             component.event_poll();
549         }
550     }
551 
552     /// Returns a pollable descriptor for the default rutabaga component. In practice, it is only
553     /// not None if the default component is virglrenderer.
poll_descriptor(&self) -> Option<SafeDescriptor>554     pub fn poll_descriptor(&self) -> Option<SafeDescriptor> {
555         let component = self.components.get(&self.default_component).or(None)?;
556         component.poll_descriptor()
557     }
558 
559     /// Creates a resource with the `resource_create_3d` metadata.
resource_create_3d( &mut self, resource_id: u32, resource_create_3d: ResourceCreate3D, ) -> RutabagaResult<()>560     pub fn resource_create_3d(
561         &mut self,
562         resource_id: u32,
563         resource_create_3d: ResourceCreate3D,
564     ) -> RutabagaResult<()> {
565         let component = self
566             .components
567             .get_mut(&self.default_component)
568             .ok_or(RutabagaError::InvalidComponent)?;
569 
570         if self.resources.contains_key(&resource_id) {
571             return Err(RutabagaError::InvalidResourceId);
572         }
573 
574         let resource = component.create_3d(resource_id, resource_create_3d)?;
575         self.resources.insert(resource_id, resource);
576         Ok(())
577     }
578 
579     /// Attaches `vecs` to the resource.
attach_backing( &mut self, resource_id: u32, mut vecs: Vec<RutabagaIovec>, ) -> RutabagaResult<()>580     pub fn attach_backing(
581         &mut self,
582         resource_id: u32,
583         mut vecs: Vec<RutabagaIovec>,
584     ) -> RutabagaResult<()> {
585         let component = self
586             .components
587             .get_mut(&self.default_component)
588             .ok_or(RutabagaError::InvalidComponent)?;
589 
590         let resource = self
591             .resources
592             .get_mut(&resource_id)
593             .ok_or(RutabagaError::InvalidResourceId)?;
594 
595         component.attach_backing(resource_id, &mut vecs)?;
596         resource.backing_iovecs = Some(vecs);
597         Ok(())
598     }
599 
600     /// Detaches any previously attached iovecs from the resource.
detach_backing(&mut self, resource_id: u32) -> RutabagaResult<()>601     pub fn detach_backing(&mut self, resource_id: u32) -> RutabagaResult<()> {
602         let component = self
603             .components
604             .get_mut(&self.default_component)
605             .ok_or(RutabagaError::InvalidComponent)?;
606 
607         let resource = self
608             .resources
609             .get_mut(&resource_id)
610             .ok_or(RutabagaError::InvalidResourceId)?;
611 
612         component.detach_backing(resource_id);
613         resource.backing_iovecs = None;
614         Ok(())
615     }
616 
617     /// Releases guest kernel reference on the resource.
unref_resource(&mut self, resource_id: u32) -> RutabagaResult<()>618     pub fn unref_resource(&mut self, resource_id: u32) -> RutabagaResult<()> {
619         let component = self
620             .components
621             .get_mut(&self.default_component)
622             .ok_or(RutabagaError::InvalidComponent)?;
623 
624         self.resources
625             .remove(&resource_id)
626             .ok_or(RutabagaError::InvalidResourceId)?;
627 
628         component.unref_resource(resource_id);
629         Ok(())
630     }
631 
632     /// For HOST3D_GUEST resources, copies from the attached iovecs to the host resource.  For
633     /// HOST3D resources, this may flush caches, though this feature is unused by guest userspace.
transfer_write( &mut self, ctx_id: u32, resource_id: u32, transfer: Transfer3D, ) -> RutabagaResult<()>634     pub fn transfer_write(
635         &mut self,
636         ctx_id: u32,
637         resource_id: u32,
638         transfer: Transfer3D,
639     ) -> RutabagaResult<()> {
640         let component = self
641             .components
642             .get(&self.default_component)
643             .ok_or(RutabagaError::InvalidComponent)?;
644 
645         let resource = self
646             .resources
647             .get_mut(&resource_id)
648             .ok_or(RutabagaError::InvalidResourceId)?;
649 
650         component.transfer_write(ctx_id, resource, transfer)
651     }
652 
653     /// 1) If specified, copies to `buf` from the host resource.
654     /// 2) Otherwise, for HOST3D_GUEST resources, copies to the attached iovecs from the host
655     ///    resource.  For HOST3D resources, this may invalidate caches, though this feature is
656     ///    unused by guest userspace.
transfer_read( &mut self, ctx_id: u32, resource_id: u32, transfer: Transfer3D, buf: Option<IoSliceMut>, ) -> RutabagaResult<()>657     pub fn transfer_read(
658         &mut self,
659         ctx_id: u32,
660         resource_id: u32,
661         transfer: Transfer3D,
662         buf: Option<IoSliceMut>,
663     ) -> RutabagaResult<()> {
664         let component = self
665             .components
666             .get(&self.default_component)
667             .ok_or(RutabagaError::InvalidComponent)?;
668 
669         let resource = self
670             .resources
671             .get_mut(&resource_id)
672             .ok_or(RutabagaError::InvalidResourceId)?;
673 
674         component.transfer_read(ctx_id, resource, transfer, buf)
675     }
676 
resource_flush(&mut self, resource_id: u32) -> RutabagaResult<()>677     pub fn resource_flush(&mut self, resource_id: u32) -> RutabagaResult<()> {
678         let component = self
679             .components
680             .get(&self.default_component)
681             .ok_or(RutabagaError::Unsupported)?;
682 
683         let resource = self
684             .resources
685             .get_mut(&resource_id)
686             .ok_or(RutabagaError::InvalidResourceId)?;
687 
688         component.resource_flush(resource)
689     }
690 
691     /// Creates a blob resource with the `ctx_id` and `resource_create_blob` metadata.
692     /// Associates `iovecs` with the resource, if there are any.  Associates externally
693     /// created `handle` with the resource, if there is any.
resource_create_blob( &mut self, ctx_id: u32, resource_id: u32, resource_create_blob: ResourceCreateBlob, iovecs: Option<Vec<RutabagaIovec>>, handle: Option<RutabagaHandle>, ) -> RutabagaResult<()>694     pub fn resource_create_blob(
695         &mut self,
696         ctx_id: u32,
697         resource_id: u32,
698         resource_create_blob: ResourceCreateBlob,
699         iovecs: Option<Vec<RutabagaIovec>>,
700         handle: Option<RutabagaHandle>,
701     ) -> RutabagaResult<()> {
702         if self.resources.contains_key(&resource_id) {
703             return Err(RutabagaError::InvalidResourceId);
704         }
705 
706         let component = self
707             .components
708             .get_mut(&self.default_component)
709             .ok_or(RutabagaError::InvalidComponent)?;
710 
711         let mut context = None;
712         // For the cross-domain context, we'll need to create the blob resource via a home-grown
713         // rutabaga context rather than one from an external C/C++ component.  Use `ctx_id` and
714         // the component type if it happens to be a cross-domain context.
715         if ctx_id > 0 {
716             let ctx = self
717                 .contexts
718                 .get_mut(&ctx_id)
719                 .ok_or(RutabagaError::InvalidContextId)?;
720 
721             if ctx.component_type() == RutabagaComponentType::CrossDomain {
722                 context = Some(ctx);
723             }
724         }
725 
726         let resource = match context {
727             Some(ctx) => ctx.context_create_blob(resource_id, resource_create_blob, handle)?,
728             None => {
729                 component.create_blob(ctx_id, resource_id, resource_create_blob, iovecs, handle)?
730             }
731         };
732 
733         self.resources.insert(resource_id, resource);
734         Ok(())
735     }
736 
737     /// Returns a memory mapping of the blob resource.
map(&mut self, resource_id: u32) -> RutabagaResult<RutabagaMapping>738     pub fn map(&mut self, resource_id: u32) -> RutabagaResult<RutabagaMapping> {
739         let resource = self
740             .resources
741             .get_mut(&resource_id)
742             .ok_or(RutabagaError::InvalidResourceId)?;
743 
744         let component_type = calculate_component(resource.component_mask)?;
745         if component_type == RutabagaComponentType::CrossDomain {
746             let handle_opt = resource.handle.take();
747             match handle_opt {
748                 Some(handle) => {
749                     if handle.handle_type != RUTABAGA_MEM_HANDLE_TYPE_SHM {
750                         return Err(RutabagaError::SpecViolation(
751                             "expected a shared memory handle",
752                         ));
753                     }
754 
755                     let clone = handle.try_clone()?;
756                     let resource_size: usize = resource.size.try_into()?;
757                     let map_info = resource
758                         .map_info
759                         .ok_or(RutabagaError::SpecViolation("no map info available"))?;
760 
761                     // Creating the mapping closes the cloned descriptor.
762                     let mapping = MemoryMapping::from_safe_descriptor(
763                         clone.os_handle,
764                         resource_size,
765                         map_info,
766                     )?;
767                     let rutabaga_mapping = mapping.as_rutabaga_mapping();
768                     resource.handle = Some(handle);
769                     resource.mapping = Some(mapping);
770 
771                     return Ok(rutabaga_mapping);
772                 }
773                 None => return Err(RutabagaError::SpecViolation("expected a handle to map")),
774             }
775         }
776 
777         let component = self
778             .components
779             .get(&component_type)
780             .ok_or(RutabagaError::InvalidComponent)?;
781 
782         component.map(resource_id)
783     }
784 
785     /// Unmaps the blob resource from the default component
unmap(&mut self, resource_id: u32) -> RutabagaResult<()>786     pub fn unmap(&mut self, resource_id: u32) -> RutabagaResult<()> {
787         let resource = self
788             .resources
789             .get_mut(&resource_id)
790             .ok_or(RutabagaError::InvalidResourceId)?;
791 
792         let component_type = calculate_component(resource.component_mask)?;
793         if component_type == RutabagaComponentType::CrossDomain {
794             resource.mapping = None;
795             return Ok(());
796         }
797 
798         let component = self
799             .components
800             .get(&component_type)
801             .ok_or(RutabagaError::InvalidComponent)?;
802 
803         component.unmap(resource_id)
804     }
805 
806     /// Returns the `map_info` of the blob resource. The valid values for `map_info`
807     /// are defined in the virtio-gpu spec.
map_info(&self, resource_id: u32) -> RutabagaResult<u32>808     pub fn map_info(&self, resource_id: u32) -> RutabagaResult<u32> {
809         let resource = self
810             .resources
811             .get(&resource_id)
812             .ok_or(RutabagaError::InvalidResourceId)?;
813 
814         resource
815             .map_info
816             .ok_or(RutabagaError::SpecViolation("no map info available"))
817     }
818 
819     /// Returns the `vulkan_info` of the blob resource, which consists of the physical device
820     /// index and memory index associated with the resource.
vulkan_info(&self, resource_id: u32) -> RutabagaResult<VulkanInfo>821     pub fn vulkan_info(&self, resource_id: u32) -> RutabagaResult<VulkanInfo> {
822         let resource = self
823             .resources
824             .get(&resource_id)
825             .ok_or(RutabagaError::InvalidResourceId)?;
826 
827         resource.vulkan_info.ok_or(RutabagaError::InvalidVulkanInfo)
828     }
829 
830     /// Returns the 3D info associated with the resource, if any.
query(&self, resource_id: u32) -> RutabagaResult<Resource3DInfo>831     pub fn query(&self, resource_id: u32) -> RutabagaResult<Resource3DInfo> {
832         let resource = self
833             .resources
834             .get(&resource_id)
835             .ok_or(RutabagaError::InvalidResourceId)?;
836 
837         resource
838             .info_3d
839             .ok_or(RutabagaError::SpecViolation("no 3d info available"))
840     }
841 
842     /// Exports a blob resource.  See virtio-gpu spec for blob flag use flags.
export_blob(&mut self, resource_id: u32) -> RutabagaResult<RutabagaHandle>843     pub fn export_blob(&mut self, resource_id: u32) -> RutabagaResult<RutabagaHandle> {
844         let resource = self
845             .resources
846             .get_mut(&resource_id)
847             .ok_or(RutabagaError::InvalidResourceId)?;
848 
849         // We can inspect blob flags only once guest minigbm is fully transitioned to blob.
850         let share_mask = RUTABAGA_BLOB_FLAG_USE_SHAREABLE | RUTABAGA_BLOB_FLAG_USE_CROSS_DEVICE;
851         let shareable = (resource.blob_flags & share_mask != 0) || !resource.blob;
852 
853         let opt = resource.handle.take();
854 
855         match (opt, shareable) {
856             (Some(handle), true) => {
857                 let clone = handle.try_clone()?;
858                 resource.handle = Some(handle);
859                 Ok(clone)
860             }
861             (Some(handle), false) => {
862                 // Exactly one strong reference in this case.
863                 let hnd =
864                     Arc::try_unwrap(handle).map_err(|_| RutabagaError::InvalidRutabagaHandle)?;
865                 Ok(hnd)
866             }
867             _ => Err(RutabagaError::InvalidRutabagaHandle),
868         }
869     }
870 
871     /// Exports the given fence for import into other processes.
export_fence(&self, fence_id: u64) -> RutabagaResult<RutabagaHandle>872     pub fn export_fence(&self, fence_id: u64) -> RutabagaResult<RutabagaHandle> {
873         let component = self
874             .components
875             .get(&self.default_component)
876             .ok_or(RutabagaError::InvalidComponent)?;
877 
878         component.export_fence(fence_id)
879     }
880 
881     /// Creates snapshotth the given `ctx_id` and `context_init` variable.
882     /// `context_init` is used to determine which rutabaga component creates the context.
create_context( &mut self, ctx_id: u32, context_init: u32, context_name: Option<&str>, ) -> RutabagaResult<()>883     pub fn create_context(
884         &mut self,
885         ctx_id: u32,
886         context_init: u32,
887         context_name: Option<&str>,
888     ) -> RutabagaResult<()> {
889         // The default workaround is just until context types are fully supported in all
890         // Google kernels.
891         let capset_id = context_init & RUTABAGA_CONTEXT_INIT_CAPSET_ID_MASK;
892         let component_type = self
893             .capset_id_to_component_type(capset_id)
894             .unwrap_or(self.default_component);
895 
896         let component = self
897             .components
898             .get_mut(&component_type)
899             .ok_or(RutabagaError::InvalidComponent)?;
900 
901         if self.contexts.contains_key(&ctx_id) {
902             return Err(RutabagaError::InvalidContextId);
903         }
904 
905         let ctx = component.create_context(
906             ctx_id,
907             context_init,
908             context_name,
909             self.fence_handler.clone(),
910         )?;
911         self.contexts.insert(ctx_id, ctx);
912         Ok(())
913     }
914 
915     /// Destroys the context given by `ctx_id`.
destroy_context(&mut self, ctx_id: u32) -> RutabagaResult<()>916     pub fn destroy_context(&mut self, ctx_id: u32) -> RutabagaResult<()> {
917         self.contexts
918             .remove(&ctx_id)
919             .ok_or(RutabagaError::InvalidContextId)?;
920         Ok(())
921     }
922 
923     /// Attaches the resource given by `resource_id` to the context given by `ctx_id`.
context_attach_resource(&mut self, ctx_id: u32, resource_id: u32) -> RutabagaResult<()>924     pub fn context_attach_resource(&mut self, ctx_id: u32, resource_id: u32) -> RutabagaResult<()> {
925         let ctx = self
926             .contexts
927             .get_mut(&ctx_id)
928             .ok_or(RutabagaError::InvalidContextId)?;
929 
930         let resource = self
931             .resources
932             .get_mut(&resource_id)
933             .ok_or(RutabagaError::InvalidResourceId)?;
934 
935         ctx.attach(resource);
936         Ok(())
937     }
938 
939     /// Detaches the resource given by `resource_id` from the context given by `ctx_id`.
context_detach_resource(&mut self, ctx_id: u32, resource_id: u32) -> RutabagaResult<()>940     pub fn context_detach_resource(&mut self, ctx_id: u32, resource_id: u32) -> RutabagaResult<()> {
941         let ctx = self
942             .contexts
943             .get_mut(&ctx_id)
944             .ok_or(RutabagaError::InvalidContextId)?;
945 
946         let resource = self
947             .resources
948             .get_mut(&resource_id)
949             .ok_or(RutabagaError::InvalidResourceId)?;
950 
951         ctx.detach(resource);
952         Ok(())
953     }
954 
955     /// submits `commands` to the context given by `ctx_id`.
submit_command( &mut self, ctx_id: u32, commands: &mut [u8], fence_ids: &[u64], ) -> RutabagaResult<()>956     pub fn submit_command(
957         &mut self,
958         ctx_id: u32,
959         commands: &mut [u8],
960         fence_ids: &[u64],
961     ) -> RutabagaResult<()> {
962         let ctx = self
963             .contexts
964             .get_mut(&ctx_id)
965             .ok_or(RutabagaError::InvalidContextId)?;
966 
967         ctx.submit_cmd(commands, fence_ids)
968     }
969 }
970 
971 /// Rutabaga Builder, following the Rust builder pattern.
972 #[derive(Clone)]
973 pub struct RutabagaBuilder {
974     display_width: u32,
975     display_height: u32,
976     default_component: RutabagaComponentType,
977     gfxstream_flags: GfxstreamFlags,
978     virglrenderer_flags: VirglRendererFlags,
979     capset_mask: u64,
980     channels: Option<Vec<RutabagaChannel>>,
981     debug_handler: Option<RutabagaDebugHandler>,
982     renderer_features: Option<String>,
983 }
984 
985 impl RutabagaBuilder {
986     /// Create new a RutabagaBuilder.
new(default_component: RutabagaComponentType, capset_mask: u64) -> RutabagaBuilder987     pub fn new(default_component: RutabagaComponentType, capset_mask: u64) -> RutabagaBuilder {
988         let virglrenderer_flags = VirglRendererFlags::new()
989             .use_thread_sync(true)
990             .use_async_fence_cb(true);
991         let gfxstream_flags = GfxstreamFlags::new();
992         RutabagaBuilder {
993             display_width: RUTABAGA_DEFAULT_WIDTH,
994             display_height: RUTABAGA_DEFAULT_HEIGHT,
995             default_component,
996             gfxstream_flags,
997             virglrenderer_flags,
998             capset_mask,
999             channels: None,
1000             debug_handler: None,
1001             renderer_features: None,
1002         }
1003     }
1004 
1005     /// Set display width for the RutabagaBuilder
set_display_width(mut self, display_width: u32) -> RutabagaBuilder1006     pub fn set_display_width(mut self, display_width: u32) -> RutabagaBuilder {
1007         self.display_width = display_width;
1008         self
1009     }
1010 
1011     /// Set display height for the RutabagaBuilder
set_display_height(mut self, display_height: u32) -> RutabagaBuilder1012     pub fn set_display_height(mut self, display_height: u32) -> RutabagaBuilder {
1013         self.display_height = display_height;
1014         self
1015     }
1016 
1017     /// Sets use EGL flags in gfxstream + virglrenderer.
set_use_egl(mut self, v: bool) -> RutabagaBuilder1018     pub fn set_use_egl(mut self, v: bool) -> RutabagaBuilder {
1019         self.gfxstream_flags = self.gfxstream_flags.use_egl(v);
1020         self.virglrenderer_flags = self.virglrenderer_flags.use_egl(v);
1021         self
1022     }
1023 
1024     /// Sets use GLES in gfxstream + virglrenderer.
set_use_gles(mut self, v: bool) -> RutabagaBuilder1025     pub fn set_use_gles(mut self, v: bool) -> RutabagaBuilder {
1026         self.gfxstream_flags = self.gfxstream_flags.use_gles(v);
1027         self.virglrenderer_flags = self.virglrenderer_flags.use_gles(v);
1028         self
1029     }
1030 
1031     /// Sets use GLX flags in gfxstream + virglrenderer.
set_use_glx(mut self, v: bool) -> RutabagaBuilder1032     pub fn set_use_glx(mut self, v: bool) -> RutabagaBuilder {
1033         self.gfxstream_flags = self.gfxstream_flags.use_glx(v);
1034         self.virglrenderer_flags = self.virglrenderer_flags.use_glx(v);
1035         self
1036     }
1037 
1038     /// Sets use surfaceless flags in gfxstream + virglrenderer.
set_use_surfaceless(mut self, v: bool) -> RutabagaBuilder1039     pub fn set_use_surfaceless(mut self, v: bool) -> RutabagaBuilder {
1040         self.gfxstream_flags = self.gfxstream_flags.use_surfaceless(v);
1041         self.virglrenderer_flags = self.virglrenderer_flags.use_surfaceless(v);
1042         self
1043     }
1044 
1045     /// Sets use Vulkan in gfxstream + virglrenderer.
set_use_vulkan(mut self, v: bool) -> RutabagaBuilder1046     pub fn set_use_vulkan(mut self, v: bool) -> RutabagaBuilder {
1047         self.gfxstream_flags = self.gfxstream_flags.use_vulkan(v);
1048         self.virglrenderer_flags = self.virglrenderer_flags.use_venus(v);
1049         self
1050     }
1051 
1052     /// Sets use external blob in gfxstream + virglrenderer.
set_use_external_blob(mut self, v: bool) -> RutabagaBuilder1053     pub fn set_use_external_blob(mut self, v: bool) -> RutabagaBuilder {
1054         self.gfxstream_flags = self.gfxstream_flags.use_external_blob(v);
1055         self.virglrenderer_flags = self.virglrenderer_flags.use_external_blob(v);
1056         self
1057     }
1058 
1059     /// Sets use system blob in gfxstream.
set_use_system_blob(mut self, v: bool) -> RutabagaBuilder1060     pub fn set_use_system_blob(mut self, v: bool) -> RutabagaBuilder {
1061         self.gfxstream_flags = self.gfxstream_flags.use_system_blob(v);
1062         self
1063     }
1064 
1065     /// Sets use render server in virglrenderer.
set_use_render_server(mut self, v: bool) -> RutabagaBuilder1066     pub fn set_use_render_server(mut self, v: bool) -> RutabagaBuilder {
1067         self.virglrenderer_flags = self.virglrenderer_flags.use_render_server(v);
1068         self
1069     }
1070 
1071     /// Use the Vulkan swapchain to draw on the host window for gfxstream.
set_wsi(mut self, v: RutabagaWsi) -> RutabagaBuilder1072     pub fn set_wsi(mut self, v: RutabagaWsi) -> RutabagaBuilder {
1073         self.gfxstream_flags = self.gfxstream_flags.set_wsi(v);
1074         self
1075     }
1076 
1077     /// Set rutabaga channels for the RutabagaBuilder
set_rutabaga_channels( mut self, channels: Option<Vec<RutabagaChannel>>, ) -> RutabagaBuilder1078     pub fn set_rutabaga_channels(
1079         mut self,
1080         channels: Option<Vec<RutabagaChannel>>,
1081     ) -> RutabagaBuilder {
1082         self.channels = channels;
1083         self
1084     }
1085 
1086     /// Set debug handler for the RutabagaBuilder
set_debug_handler( mut self, debug_handler: Option<RutabagaDebugHandler>, ) -> RutabagaBuilder1087     pub fn set_debug_handler(
1088         mut self,
1089         debug_handler: Option<RutabagaDebugHandler>,
1090     ) -> RutabagaBuilder {
1091         self.debug_handler = debug_handler;
1092         self
1093     }
1094 
1095     /// Set renderer features for the RutabagaBuilder
set_renderer_features(mut self, renderer_features: Option<String>) -> RutabagaBuilder1096     pub fn set_renderer_features(mut self, renderer_features: Option<String>) -> RutabagaBuilder {
1097         self.renderer_features = renderer_features;
1098         self
1099     }
1100 
1101     /// Builds Rutabaga and returns a handle to it.
1102     ///
1103     /// This should be only called once per every virtual machine instance.  Rutabaga tries to
1104     /// intialize all 3D components which have been built. In 2D mode, only the 2D component is
1105     /// initialized.
build( mut self, fence_handler: RutabagaFenceHandler, #[allow(unused_variables)] rutabaga_server_descriptor: Option<SafeDescriptor>, ) -> RutabagaResult<Rutabaga>1106     pub fn build(
1107         mut self,
1108         fence_handler: RutabagaFenceHandler,
1109         #[allow(unused_variables)] rutabaga_server_descriptor: Option<SafeDescriptor>,
1110     ) -> RutabagaResult<Rutabaga> {
1111         let mut rutabaga_components: Map<RutabagaComponentType, Box<dyn RutabagaComponent>> =
1112             Default::default();
1113 
1114         #[allow(unused_mut)]
1115         let mut rutabaga_capsets: Vec<RutabagaCapsetInfo> = Default::default();
1116 
1117         let capset_enabled =
1118             |capset_id: u32| -> bool { (self.capset_mask & (1 << capset_id)) != 0 };
1119 
1120         let mut push_capset = |capset_id: u32| {
1121             if let Some(capset) = RUTABAGA_CAPSETS
1122                 .iter()
1123                 .find(|capset| capset_id == capset.capset_id)
1124             {
1125                 if self.capset_mask != 0 {
1126                     if capset_enabled(capset.capset_id) {
1127                         rutabaga_capsets.push(*capset);
1128                     }
1129                 } else {
1130                     // Unconditionally push capset -- this should eventually be deleted when context
1131                     // types are always specified by crosvm launchers.
1132                     rutabaga_capsets.push(*capset);
1133                 }
1134             };
1135         };
1136 
1137         if self.capset_mask != 0 {
1138             let supports_gfxstream = capset_enabled(RUTABAGA_CAPSET_GFXSTREAM_VULKAN)
1139                 | capset_enabled(RUTABAGA_CAPSET_GFXSTREAM_MAGMA)
1140                 | capset_enabled(RUTABAGA_CAPSET_GFXSTREAM_GLES)
1141                 | capset_enabled(RUTABAGA_CAPSET_GFXSTREAM_COMPOSER);
1142             let supports_virglrenderer = capset_enabled(RUTABAGA_CAPSET_VIRGL2)
1143                 | capset_enabled(RUTABAGA_CAPSET_VENUS)
1144                 | capset_enabled(RUTABAGA_CAPSET_DRM);
1145 
1146             if supports_gfxstream {
1147                 self.default_component = RutabagaComponentType::Gfxstream;
1148             } else if supports_virglrenderer {
1149                 self.default_component = RutabagaComponentType::VirglRenderer;
1150             } else {
1151                 self.default_component = RutabagaComponentType::CrossDomain;
1152             }
1153 
1154             self.virglrenderer_flags = self
1155                 .virglrenderer_flags
1156                 .use_virgl(capset_enabled(RUTABAGA_CAPSET_VIRGL2))
1157                 .use_venus(capset_enabled(RUTABAGA_CAPSET_VENUS))
1158                 .use_drm(capset_enabled(RUTABAGA_CAPSET_DRM));
1159 
1160             self.gfxstream_flags = self
1161                 .gfxstream_flags
1162                 .use_gles(capset_enabled(RUTABAGA_CAPSET_GFXSTREAM_GLES))
1163                 .use_vulkan(capset_enabled(RUTABAGA_CAPSET_GFXSTREAM_VULKAN))
1164         }
1165 
1166         // Make sure that disabled components are not used as default.
1167         #[cfg(not(feature = "virgl_renderer"))]
1168         if self.default_component == RutabagaComponentType::VirglRenderer {
1169             return Err(RutabagaError::InvalidRutabagaBuild(
1170                 "virgl renderer feature not enabled",
1171             ));
1172         }
1173         #[cfg(not(feature = "gfxstream"))]
1174         if self.default_component == RutabagaComponentType::Gfxstream {
1175             return Err(RutabagaError::InvalidRutabagaBuild(
1176                 "gfxstream feature not enabled",
1177             ));
1178         }
1179 
1180         if self.default_component == RutabagaComponentType::Rutabaga2D {
1181             let rutabaga_2d = Rutabaga2D::init(fence_handler.clone())?;
1182             rutabaga_components.insert(RutabagaComponentType::Rutabaga2D, rutabaga_2d);
1183         } else {
1184             #[cfg(feature = "virgl_renderer")]
1185             if self.default_component == RutabagaComponentType::VirglRenderer {
1186                 let virgl = VirglRenderer::init(
1187                     self.virglrenderer_flags,
1188                     fence_handler.clone(),
1189                     rutabaga_server_descriptor,
1190                 )?;
1191                 rutabaga_components.insert(RutabagaComponentType::VirglRenderer, virgl);
1192 
1193                 push_capset(RUTABAGA_CAPSET_VIRGL);
1194                 push_capset(RUTABAGA_CAPSET_VIRGL2);
1195                 push_capset(RUTABAGA_CAPSET_VENUS);
1196                 push_capset(RUTABAGA_CAPSET_DRM);
1197             }
1198 
1199             #[cfg(feature = "gfxstream")]
1200             if self.default_component == RutabagaComponentType::Gfxstream {
1201                 let gfxstream = Gfxstream::init(
1202                     self.display_width,
1203                     self.display_height,
1204                     self.gfxstream_flags,
1205                     self.renderer_features,
1206                     fence_handler.clone(),
1207                     self.debug_handler.clone(),
1208                 )?;
1209 
1210                 rutabaga_components.insert(RutabagaComponentType::Gfxstream, gfxstream);
1211 
1212                 push_capset(RUTABAGA_CAPSET_GFXSTREAM_VULKAN);
1213                 push_capset(RUTABAGA_CAPSET_GFXSTREAM_MAGMA);
1214                 push_capset(RUTABAGA_CAPSET_GFXSTREAM_GLES);
1215                 push_capset(RUTABAGA_CAPSET_GFXSTREAM_COMPOSER);
1216             }
1217 
1218             let cross_domain = CrossDomain::init(self.channels, fence_handler.clone())?;
1219             rutabaga_components.insert(RutabagaComponentType::CrossDomain, cross_domain);
1220             push_capset(RUTABAGA_CAPSET_CROSS_DOMAIN);
1221         }
1222 
1223         Ok(Rutabaga {
1224             resources: Default::default(),
1225             contexts: Default::default(),
1226             components: rutabaga_components,
1227             default_component: self.default_component,
1228             capset_info: rutabaga_capsets,
1229             fence_handler,
1230         })
1231     }
1232 }
1233 
1234 #[cfg(test)]
1235 mod tests {
1236     use crate::*;
1237 
new_2d() -> Rutabaga1238     fn new_2d() -> Rutabaga {
1239         RutabagaBuilder::new(RutabagaComponentType::Rutabaga2D, 0)
1240             .build(RutabagaHandler::new(|_| {}), None)
1241             .unwrap()
1242     }
1243 
1244     #[test]
snapshot_restore_2d_no_resources()1245     fn snapshot_restore_2d_no_resources() {
1246         let mut buffer = std::io::Cursor::new(Vec::new());
1247 
1248         let rutabaga1 = new_2d();
1249         rutabaga1.snapshot(&mut buffer, "").unwrap();
1250 
1251         let mut rutabaga1 = new_2d();
1252         rutabaga1.restore(&mut &buffer.get_ref()[..], "").unwrap();
1253     }
1254 
1255     #[test]
snapshot_restore_2d_one_resource()1256     fn snapshot_restore_2d_one_resource() {
1257         let resource_id = 123;
1258         let resource_create_3d = ResourceCreate3D {
1259             target: RUTABAGA_PIPE_TEXTURE_2D,
1260             format: 1,
1261             bind: RUTABAGA_PIPE_BIND_RENDER_TARGET,
1262             width: 100,
1263             height: 200,
1264             depth: 1,
1265             array_size: 1,
1266             last_level: 0,
1267             nr_samples: 0,
1268             flags: 0,
1269         };
1270 
1271         let mut buffer = std::io::Cursor::new(Vec::new());
1272 
1273         let mut rutabaga1 = new_2d();
1274         rutabaga1
1275             .resource_create_3d(resource_id, resource_create_3d)
1276             .unwrap();
1277         rutabaga1
1278             .attach_backing(
1279                 resource_id,
1280                 vec![RutabagaIovec {
1281                     base: std::ptr::null_mut(),
1282                     len: 456,
1283                 }],
1284             )
1285             .unwrap();
1286         rutabaga1.snapshot(&mut buffer, "").unwrap();
1287 
1288         let mut rutabaga2 = new_2d();
1289         rutabaga2.restore(&mut &buffer.get_ref()[..], "").unwrap();
1290 
1291         assert_eq!(rutabaga2.resources.len(), 1);
1292         let rutabaga_resource = rutabaga2.resources.get(&resource_id).unwrap();
1293         assert_eq!(rutabaga_resource.resource_id, resource_id);
1294         assert_eq!(
1295             rutabaga_resource.info_2d.as_ref().unwrap().width,
1296             resource_create_3d.width
1297         );
1298         assert_eq!(
1299             rutabaga_resource.info_2d.as_ref().unwrap().height,
1300             resource_create_3d.height
1301         );
1302         // NOTE: We attached an backing iovec, but it should be gone post-restore.
1303         assert!(rutabaga_resource.backing_iovecs.is_none());
1304     }
1305 }
1306