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