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