1 // Copyright 2020 The Chromium OS Authors. All rights reserved.
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 
7 use std::collections::BTreeMap as Map;
8 use std::sync::Arc;
9 
10 use base::ExternalMapping;
11 use data_model::VolatileSlice;
12 
13 use crate::cross_domain::CrossDomain;
14 
15 #[cfg(feature = "gfxstream")]
16 use crate::gfxstream::Gfxstream;
17 
18 use crate::rutabaga_2d::Rutabaga2D;
19 use crate::rutabaga_utils::*;
20 
21 #[cfg(feature = "virgl_renderer")]
22 use crate::virgl_renderer::VirglRenderer;
23 
24 /// Information required for 2D functionality.
25 pub struct Rutabaga2DInfo {
26     pub width: u32,
27     pub height: u32,
28     pub host_mem: Vec<u8>,
29 }
30 
31 /// A Rutabaga resource, supporting 2D and 3D rutabaga features.  Assumes a single-threaded library.
32 pub struct RutabagaResource {
33     pub resource_id: u32,
34     pub handle: Option<Arc<RutabagaHandle>>,
35     pub blob: bool,
36     pub blob_mem: u32,
37     pub blob_flags: u32,
38     pub map_info: Option<u32>,
39     pub info_2d: Option<Rutabaga2DInfo>,
40     pub info_3d: Option<Resource3DInfo>,
41     pub vulkan_info: Option<VulkanInfo>,
42     pub backing_iovecs: Option<Vec<RutabagaIovec>>,
43 }
44 
45 /// A RutabagaComponent is a building block of the Virtual Graphics Interface (VGI).  Each component
46 /// on it's own is sufficient to virtualize graphics on many Google products.  These components wrap
47 /// libraries like gfxstream or virglrenderer, and Rutabaga's own 2D and cross-domain prototype
48 /// functionality.
49 ///
50 /// Most methods return a `RutabagaResult` that indicate the success, failure, or requested data for
51 /// the given command.
52 pub trait RutabagaComponent {
53     /// Implementations should return the version and size of the given capset_id.  (0, 0) is
54     /// returned by default.
get_capset_info(&self, _capset_id: u32) -> (u32, u32)55     fn get_capset_info(&self, _capset_id: u32) -> (u32, u32) {
56         (0, 0)
57     }
58 
59     /// Implementations should return the capabilites of given a `capset_id` and `version`.  A
60     /// zero-sized array is returned by default.
get_capset(&self, _capset_id: u32, _version: u32) -> Vec<u8>61     fn get_capset(&self, _capset_id: u32, _version: u32) -> Vec<u8> {
62         Vec::new()
63     }
64 
65     /// Implementations should set their internal context to be the reserved context 0.
force_ctx_0(&self)66     fn force_ctx_0(&self) {}
67 
68     /// Implementations must create a fence that represents the completion of prior work.  This is
69     /// required for synchronization with the guest kernel.
create_fence(&mut self, _fence_data: RutabagaFenceData) -> RutabagaResult<()>70     fn create_fence(&mut self, _fence_data: RutabagaFenceData) -> RutabagaResult<()> {
71         Err(RutabagaError::Unsupported)
72     }
73 
74     /// Implementations must return the last completed fence_id.
poll(&self) -> u3275     fn poll(&self) -> u32 {
76         0
77     }
78 
79     /// Implementations must create a resource with the given metadata.  For 2D rutabaga components,
80     /// this a system memory allocation.  For 3D components, this is typically a GL texture or
81     /// buffer.  Vulkan components should use blob resources instead.
create_3d( &self, _resource_id: u32, _resource_create_3d: ResourceCreate3D, ) -> RutabagaResult<RutabagaResource>82     fn create_3d(
83         &self,
84         _resource_id: u32,
85         _resource_create_3d: ResourceCreate3D,
86     ) -> RutabagaResult<RutabagaResource> {
87         Err(RutabagaError::Unsupported)
88     }
89 
90     /// Implementations must attach `vecs` to the resource.
attach_backing( &self, _resource_id: u32, _vecs: &mut Vec<RutabagaIovec>, ) -> RutabagaResult<()>91     fn attach_backing(
92         &self,
93         _resource_id: u32,
94         _vecs: &mut Vec<RutabagaIovec>,
95     ) -> RutabagaResult<()> {
96         Ok(())
97     }
98 
99     /// Implementations must detach `vecs` from the resource.
detach_backing(&self, _resource_id: u32)100     fn detach_backing(&self, _resource_id: u32) {}
101 
102     /// Implementations must release the guest kernel reference on the resource.
unref_resource(&self, _resource_id: u32)103     fn unref_resource(&self, _resource_id: u32) {}
104 
105     /// Implementations must perform the transfer write operation.  For 2D rutabaga components, this
106     /// done via memcpy().  For 3D components, this is typically done via glTexSubImage(..).
transfer_write( &self, _ctx_id: u32, _resource: &mut RutabagaResource, _transfer: Transfer3D, ) -> RutabagaResult<()>107     fn transfer_write(
108         &self,
109         _ctx_id: u32,
110         _resource: &mut RutabagaResource,
111         _transfer: Transfer3D,
112     ) -> RutabagaResult<()> {
113         Err(RutabagaError::Unsupported)
114     }
115 
116     /// Implementations must perform the transfer read operation.  For 2D rutabaga components, this
117     /// 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<VolatileSlice>, ) -> RutabagaResult<()>118     fn transfer_read(
119         &self,
120         _ctx_id: u32,
121         _resource: &mut RutabagaResource,
122         _transfer: Transfer3D,
123         _buf: Option<VolatileSlice>,
124     ) -> RutabagaResult<()> {
125         Err(RutabagaError::Unsupported)
126     }
127 
128     /// Implementations must create a blob resource on success.  The memory parameters, size, and
129     /// 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>>, ) -> RutabagaResult<RutabagaResource>130     fn create_blob(
131         &mut self,
132         _ctx_id: u32,
133         _resource_id: u32,
134         _resource_create_blob: ResourceCreateBlob,
135         _iovec_opt: Option<Vec<RutabagaIovec>>,
136     ) -> RutabagaResult<RutabagaResource> {
137         Err(RutabagaError::Unsupported)
138     }
139 
140     /// Implementations must map the blob resource on success.  This is typically done by
141     /// glMapBufferRange(...) or vkMapMemory.
map(&self, _resource_id: u32) -> RutabagaResult<ExternalMapping>142     fn map(&self, _resource_id: u32) -> RutabagaResult<ExternalMapping> {
143         Err(RutabagaError::Unsupported)
144     }
145 
146     /// Implementations must return a RutabagaHandle of the fence on success.
export_fence(&self, _fence_id: u32) -> RutabagaResult<RutabagaHandle>147     fn export_fence(&self, _fence_id: u32) -> RutabagaResult<RutabagaHandle> {
148         Err(RutabagaError::Unsupported)
149     }
150 
151     /// Implementations must create a context for submitting commands.  The command stream of the
152     /// context is determined by `context_init`.  For virgl contexts, it is a Gallium/TGSI command
153     /// stream.  For gfxstream contexts, it's an autogenerated Vulkan or GLES streams.
create_context( &self, _ctx_id: u32, _context_init: u32, ) -> RutabagaResult<Box<dyn RutabagaContext>>154     fn create_context(
155         &self,
156         _ctx_id: u32,
157         _context_init: u32,
158     ) -> RutabagaResult<Box<dyn RutabagaContext>> {
159         Err(RutabagaError::Unsupported)
160     }
161 }
162 
163 pub trait RutabagaContext {
164     /// Implementations must return a RutabagaResource given the `resource_create_blob` parameters.
context_create_blob( &mut self, _resource_id: u32, _resource_create_blob: ResourceCreateBlob, _handle: Option<RutabagaHandle>, ) -> RutabagaResult<RutabagaResource>165     fn context_create_blob(
166         &mut self,
167         _resource_id: u32,
168         _resource_create_blob: ResourceCreateBlob,
169         _handle: Option<RutabagaHandle>,
170     ) -> RutabagaResult<RutabagaResource> {
171         Err(RutabagaError::Unsupported)
172     }
173 
174     /// Implementations must handle the context-specific command stream.
submit_cmd(&mut self, _commands: &mut [u8]) -> RutabagaResult<()>175     fn submit_cmd(&mut self, _commands: &mut [u8]) -> RutabagaResult<()>;
176 
177     /// Implementations may use `resource` in this context's command stream.
attach(&mut self, _resource: &mut RutabagaResource)178     fn attach(&mut self, _resource: &mut RutabagaResource);
179 
180     /// Implementations must stop using `resource` in this context's command stream.
detach(&mut self, _resource: &RutabagaResource)181     fn detach(&mut self, _resource: &RutabagaResource);
182 
183     /// Implementations must create a fence on specified `fence_ctx_idx` in `fence_data`.  This
184     /// allows for multiple syncrhonizations timelines per RutabagaContext.
context_create_fence(&mut self, _fence_data: RutabagaFenceData) -> RutabagaResult<()>185     fn context_create_fence(&mut self, _fence_data: RutabagaFenceData) -> RutabagaResult<()> {
186         Err(RutabagaError::Unsupported)
187     }
188 
189     /// Implementations must return an array of fences that have completed.  This will be used by
190     /// the cross-domain context for asynchronous Tx/Rx.
context_poll(&mut self) -> Option<Vec<RutabagaFenceData>>191     fn context_poll(&mut self) -> Option<Vec<RutabagaFenceData>> {
192         None
193     }
194 }
195 
196 #[derive(Copy, Clone)]
197 struct RutabagaCapsetInfo {
198     pub capset_id: u32,
199     pub component: RutabagaComponentType,
200 }
201 
202 /// The global libary handle used to query capability sets, create resources and contexts.
203 ///
204 /// Currently, Rutabaga only supports one default component.  Many components running at the
205 /// same time is a stretch goal of Rutabaga GFX.
206 ///
207 /// Not thread-safe, but can be made so easily.  Making non-Rutabaga, C/C++ components
208 /// thread-safe is more difficult.
209 pub struct Rutabaga {
210     resources: Map<u32, RutabagaResource>,
211     components: Map<RutabagaComponentType, Box<dyn RutabagaComponent>>,
212     contexts: Map<u32, Box<dyn RutabagaContext>>,
213     default_component: RutabagaComponentType,
214     capset_info: Vec<RutabagaCapsetInfo>,
215 }
216 
217 impl Rutabaga {
capset_id_to_component_type(&self, capset_id: u32) -> RutabagaResult<RutabagaComponentType>218     fn capset_id_to_component_type(&self, capset_id: u32) -> RutabagaResult<RutabagaComponentType> {
219         let component = self
220             .capset_info
221             .iter()
222             .find(|capset_info| capset_info.capset_id == capset_id)
223             .ok_or(RutabagaError::InvalidCapset)?
224             .component;
225 
226         Ok(component)
227     }
228 
capset_index_to_component_info(&self, index: u32) -> RutabagaResult<RutabagaCapsetInfo>229     fn capset_index_to_component_info(&self, index: u32) -> RutabagaResult<RutabagaCapsetInfo> {
230         let idx = index as usize;
231         if idx >= self.capset_info.len() {
232             return Err(RutabagaError::InvalidCapset);
233         }
234 
235         Ok(self.capset_info[idx])
236     }
237 
238     /// Gets the version and size for the capabilty set `index`.
get_capset_info(&self, index: u32) -> RutabagaResult<(u32, u32, u32)>239     pub fn get_capset_info(&self, index: u32) -> RutabagaResult<(u32, u32, u32)> {
240         let capset_info = self.capset_index_to_component_info(index)?;
241 
242         let component = self
243             .components
244             .get(&capset_info.component)
245             .ok_or(RutabagaError::InvalidComponent)?;
246 
247         let (capset_version, capset_size) = component.get_capset_info(capset_info.capset_id);
248         Ok((capset_info.capset_id, capset_version, capset_size))
249     }
250 
251     /// Gets the capability set for the `capset_id` and `version`.
252     /// Each capability set is associated with a context type, which is associated
253     /// with a rutabaga component.
get_capset(&self, capset_id: u32, version: u32) -> RutabagaResult<Vec<u8>>254     pub fn get_capset(&self, capset_id: u32, version: u32) -> RutabagaResult<Vec<u8>> {
255         // The default workaround is just until context types are fully supported in all
256         // Google kernels.
257         let component_type = self
258             .capset_id_to_component_type(capset_id)
259             .unwrap_or(self.default_component);
260 
261         let component = self
262             .components
263             .get(&component_type)
264             .ok_or(RutabagaError::InvalidComponent)?;
265 
266         Ok(component.get_capset(capset_id, version))
267     }
268 
269     /// Forces context zero for the default rutabaga component.
force_ctx_0(&self)270     pub fn force_ctx_0(&self) {
271         if let Some(component) = self.components.get(&self.default_component) {
272             component.force_ctx_0();
273         }
274     }
275 
276     /// Creates a fence with the given `fence_data`.
277     /// If the flags include RUTABAGA_FLAG_PARAM_FENCE_CTX_IDX, then the fence is created on a
278     /// specific timeline on the specific context.
create_fence(&mut self, fence_data: RutabagaFenceData) -> RutabagaResult<()>279     pub fn create_fence(&mut self, fence_data: RutabagaFenceData) -> RutabagaResult<()> {
280         if fence_data.flags & RUTABAGA_FLAG_INFO_FENCE_CTX_IDX != 0 {
281             let ctx = self
282                 .contexts
283                 .get_mut(&fence_data.ctx_id)
284                 .ok_or(RutabagaError::InvalidContextId)?;
285 
286             ctx.context_create_fence(fence_data)?;
287         } else {
288             let component = self
289                 .components
290                 .get_mut(&self.default_component)
291                 .ok_or(RutabagaError::InvalidComponent)?;
292 
293             component.create_fence(fence_data)?;
294         }
295 
296         Ok(())
297     }
298 
299     /// Polls all rutabaga components and contexts, and returns a vector of RutabagaFenceData
300     /// describing which fences have completed.
poll(&mut self) -> Vec<RutabagaFenceData>301     pub fn poll(&mut self) -> Vec<RutabagaFenceData> {
302         let mut completed_fences: Vec<RutabagaFenceData> = Vec::new();
303         // Poll the default component -- this the global timeline which does not take into account
304         // `ctx_id` or `fence_ctx_idx`.  This path exists for OpenGL legacy reasons and 2D mode.
305         let component = self
306             .components
307             .get_mut(&self.default_component)
308             .ok_or(0)
309             .unwrap();
310 
311         let global_fence_id = component.poll();
312         completed_fences.push(RutabagaFenceData {
313             flags: RUTABAGA_FLAG_FENCE,
314             fence_id: global_fence_id as u64,
315             ctx_id: 0,
316             fence_ctx_idx: 0,
317         });
318 
319         for ctx in self.contexts.values_mut() {
320             if let Some(ref mut ctx_completed_fences) = ctx.context_poll() {
321                 completed_fences.append(ctx_completed_fences);
322             }
323         }
324         completed_fences
325     }
326 
327     /// Creates a resource with the `resource_create_3d` metadata.
resource_create_3d( &mut self, resource_id: u32, resource_create_3d: ResourceCreate3D, ) -> RutabagaResult<()>328     pub fn resource_create_3d(
329         &mut self,
330         resource_id: u32,
331         resource_create_3d: ResourceCreate3D,
332     ) -> RutabagaResult<()> {
333         let component = self
334             .components
335             .get_mut(&self.default_component)
336             .ok_or(RutabagaError::InvalidComponent)?;
337 
338         if self.resources.contains_key(&resource_id) {
339             return Err(RutabagaError::InvalidResourceId);
340         }
341 
342         let resource = component.create_3d(resource_id, resource_create_3d)?;
343         self.resources.insert(resource_id, resource);
344         Ok(())
345     }
346 
347     /// Attaches `vecs` to the resource.
attach_backing( &mut self, resource_id: u32, mut vecs: Vec<RutabagaIovec>, ) -> RutabagaResult<()>348     pub fn attach_backing(
349         &mut self,
350         resource_id: u32,
351         mut vecs: Vec<RutabagaIovec>,
352     ) -> RutabagaResult<()> {
353         let component = self
354             .components
355             .get_mut(&self.default_component)
356             .ok_or(RutabagaError::InvalidComponent)?;
357 
358         let mut resource = self
359             .resources
360             .get_mut(&resource_id)
361             .ok_or(RutabagaError::InvalidResourceId)?;
362 
363         component.attach_backing(resource_id, &mut vecs)?;
364         resource.backing_iovecs = Some(vecs);
365         Ok(())
366     }
367 
368     /// Detaches any previously attached iovecs from the resource.
detach_backing(&mut self, resource_id: u32) -> RutabagaResult<()>369     pub fn detach_backing(&mut self, resource_id: u32) -> RutabagaResult<()> {
370         let component = self
371             .components
372             .get_mut(&self.default_component)
373             .ok_or(RutabagaError::InvalidComponent)?;
374 
375         let resource = self
376             .resources
377             .get_mut(&resource_id)
378             .ok_or(RutabagaError::InvalidResourceId)?;
379 
380         component.detach_backing(resource_id);
381         resource.backing_iovecs = None;
382         Ok(())
383     }
384 
385     /// Releases guest kernel reference on the resource.
unref_resource(&mut self, resource_id: u32) -> RutabagaResult<()>386     pub fn unref_resource(&mut self, resource_id: u32) -> RutabagaResult<()> {
387         let component = self
388             .components
389             .get_mut(&self.default_component)
390             .ok_or(RutabagaError::InvalidComponent)?;
391 
392         self.resources
393             .remove(&resource_id)
394             .ok_or(RutabagaError::InvalidResourceId)?;
395 
396         component.unref_resource(resource_id);
397         Ok(())
398     }
399 
400     /// For HOST3D_GUEST resources, copies from the attached iovecs to the host resource.  For
401     /// 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<()>402     pub fn transfer_write(
403         &mut self,
404         ctx_id: u32,
405         resource_id: u32,
406         transfer: Transfer3D,
407     ) -> RutabagaResult<()> {
408         let component = self
409             .components
410             .get(&self.default_component)
411             .ok_or(RutabagaError::InvalidComponent)?;
412 
413         let resource = self
414             .resources
415             .get_mut(&resource_id)
416             .ok_or(RutabagaError::InvalidResourceId)?;
417 
418         component.transfer_write(ctx_id, resource, transfer)
419     }
420 
421     /// 1) If specified, copies to `buf` from the host resource.
422     /// 2) Otherwise, for HOST3D_GUEST resources, copies to the attached iovecs from the host
423     ///    resource.  For HOST3D resources, this may invalidate caches, though this feature is
424     ///    unused by guest userspace.
transfer_read( &mut self, ctx_id: u32, resource_id: u32, transfer: Transfer3D, buf: Option<VolatileSlice>, ) -> RutabagaResult<()>425     pub fn transfer_read(
426         &mut self,
427         ctx_id: u32,
428         resource_id: u32,
429         transfer: Transfer3D,
430         buf: Option<VolatileSlice>,
431     ) -> RutabagaResult<()> {
432         let component = self
433             .components
434             .get(&self.default_component)
435             .ok_or(RutabagaError::InvalidComponent)?;
436 
437         let resource = self
438             .resources
439             .get_mut(&resource_id)
440             .ok_or(RutabagaError::InvalidResourceId)?;
441 
442         component.transfer_read(ctx_id, resource, transfer, buf)
443     }
444 
445     /// Creates a blob resource with the `ctx_id` and `resource_create_blob` metadata.
446     /// Associates `iovecs` with the resource, if there are any.  Associates externally
447     /// 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<()>448     pub fn resource_create_blob(
449         &mut self,
450         ctx_id: u32,
451         resource_id: u32,
452         resource_create_blob: ResourceCreateBlob,
453         iovecs: Option<Vec<RutabagaIovec>>,
454         handle: Option<RutabagaHandle>,
455     ) -> RutabagaResult<()> {
456         if self.resources.contains_key(&resource_id) {
457             return Err(RutabagaError::InvalidResourceId);
458         }
459 
460         // For the cross-domain context, we'll need to create the blob resource via a home-grown
461         // rutabaga context rather than one from an external C/C++ component.  Use `ctx_id` to check
462         // if it happens to be a cross-domain context.
463         if ctx_id > 0 {
464             let ctx = self
465                 .contexts
466                 .get_mut(&ctx_id)
467                 .ok_or(RutabagaError::InvalidContextId)?;
468 
469             if let Ok(resource) = ctx.context_create_blob(resource_id, resource_create_blob, handle)
470             {
471                 self.resources.insert(resource_id, resource);
472                 return Ok(());
473             }
474         }
475 
476         let component = self
477             .components
478             .get_mut(&self.default_component)
479             .ok_or(RutabagaError::InvalidComponent)?;
480 
481         let resource = component.create_blob(ctx_id, resource_id, resource_create_blob, iovecs)?;
482         self.resources.insert(resource_id, resource);
483         Ok(())
484     }
485 
486     /// Returns a memory mapping of the blob resource.
map(&self, resource_id: u32) -> RutabagaResult<ExternalMapping>487     pub fn map(&self, resource_id: u32) -> RutabagaResult<ExternalMapping> {
488         let component = self
489             .components
490             .get(&self.default_component)
491             .ok_or(RutabagaError::InvalidComponent)?;
492 
493         if !self.resources.contains_key(&resource_id) {
494             return Err(RutabagaError::InvalidResourceId);
495         }
496 
497         component.map(resource_id)
498     }
499 
500     /// Returns the `map_info` of the blob resource. The valid values for `map_info`
501     /// are defined in the virtio-gpu spec.
map_info(&self, resource_id: u32) -> RutabagaResult<u32>502     pub fn map_info(&self, resource_id: u32) -> RutabagaResult<u32> {
503         let resource = self
504             .resources
505             .get(&resource_id)
506             .ok_or(RutabagaError::InvalidResourceId)?;
507 
508         resource.map_info.ok_or(RutabagaError::SpecViolation)
509     }
510 
511     /// Returns the `vulkan_info` of the blob resource, which consists of the physical device
512     /// index and memory index associated with the resource.
vulkan_info(&self, resource_id: u32) -> RutabagaResult<VulkanInfo>513     pub fn vulkan_info(&self, resource_id: u32) -> RutabagaResult<VulkanInfo> {
514         let resource = self
515             .resources
516             .get(&resource_id)
517             .ok_or(RutabagaError::InvalidResourceId)?;
518 
519         resource.vulkan_info.ok_or(RutabagaError::Unsupported)
520     }
521 
522     /// Returns the 3D info associated with the resource, if any.
query(&self, resource_id: u32) -> RutabagaResult<Resource3DInfo>523     pub fn query(&self, resource_id: u32) -> RutabagaResult<Resource3DInfo> {
524         let resource = self
525             .resources
526             .get(&resource_id)
527             .ok_or(RutabagaError::InvalidResourceId)?;
528 
529         resource.info_3d.ok_or(RutabagaError::Unsupported)
530     }
531 
532     /// Exports a blob resource.  See virtio-gpu spec for blob flag use flags.
export_blob(&mut self, resource_id: u32) -> RutabagaResult<RutabagaHandle>533     pub fn export_blob(&mut self, resource_id: u32) -> RutabagaResult<RutabagaHandle> {
534         let resource = self
535             .resources
536             .get_mut(&resource_id)
537             .ok_or(RutabagaError::InvalidResourceId)?;
538 
539         // We can inspect blob flags only once guest minigbm is fully transitioned to blob.
540         let share_mask = RUTABAGA_BLOB_FLAG_USE_SHAREABLE | RUTABAGA_BLOB_FLAG_USE_CROSS_DEVICE;
541         let shareable = (resource.blob_flags & share_mask != 0) || !resource.blob;
542 
543         let opt = resource.handle.take();
544 
545         match (opt, shareable) {
546             (Some(handle), true) => {
547                 let clone = handle.try_clone()?;
548                 resource.handle = Some(handle);
549                 Ok(clone)
550             }
551             (Some(handle), false) => {
552                 // Exactly one strong reference in this case.
553                 let hnd = Arc::try_unwrap(handle).map_err(|_| RutabagaError::SpecViolation)?;
554                 Ok(hnd)
555             }
556             _ => Err(RutabagaError::Unsupported),
557         }
558     }
559 
560     /// Exports the given fence for import into other processes.
export_fence(&self, fence_id: u32) -> RutabagaResult<RutabagaHandle>561     pub fn export_fence(&self, fence_id: u32) -> RutabagaResult<RutabagaHandle> {
562         let component = self
563             .components
564             .get(&self.default_component)
565             .ok_or(RutabagaError::InvalidComponent)?;
566 
567         component.export_fence(fence_id)
568     }
569 
570     /// Creates a context with the given `ctx_id` and `context_init` variable.
571     /// `context_init` is used to determine which rutabaga component creates the context.
create_context(&mut self, ctx_id: u32, context_init: u32) -> RutabagaResult<()>572     pub fn create_context(&mut self, ctx_id: u32, context_init: u32) -> RutabagaResult<()> {
573         // The default workaround is just until context types are fully supported in all
574         // Google kernels.
575         let capset_id = context_init & RUTABAGA_CONTEXT_INIT_CAPSET_ID_MASK;
576         let component_type = self
577             .capset_id_to_component_type(capset_id)
578             .unwrap_or(self.default_component);
579 
580         let component = self
581             .components
582             .get_mut(&component_type)
583             .ok_or(RutabagaError::InvalidComponent)?;
584 
585         if self.contexts.contains_key(&ctx_id) {
586             return Err(RutabagaError::InvalidContextId);
587         }
588 
589         let ctx = component.create_context(ctx_id, context_init)?;
590         self.contexts.insert(ctx_id, ctx);
591         Ok(())
592     }
593 
594     /// Destroys the context given by `ctx_id`.
destroy_context(&mut self, ctx_id: u32) -> RutabagaResult<()>595     pub fn destroy_context(&mut self, ctx_id: u32) -> RutabagaResult<()> {
596         self.contexts
597             .remove(&ctx_id)
598             .ok_or(RutabagaError::InvalidContextId)?;
599         Ok(())
600     }
601 
602     /// 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<()>603     pub fn context_attach_resource(&mut self, ctx_id: u32, resource_id: u32) -> RutabagaResult<()> {
604         let ctx = self
605             .contexts
606             .get_mut(&ctx_id)
607             .ok_or(RutabagaError::InvalidContextId)?;
608 
609         let mut resource = self
610             .resources
611             .get_mut(&resource_id)
612             .ok_or(RutabagaError::InvalidResourceId)?;
613 
614         ctx.attach(&mut resource);
615         Ok(())
616     }
617 
618     /// 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<()>619     pub fn context_detach_resource(&mut self, ctx_id: u32, resource_id: u32) -> RutabagaResult<()> {
620         let ctx = self
621             .contexts
622             .get_mut(&ctx_id)
623             .ok_or(RutabagaError::InvalidContextId)?;
624 
625         let resource = self
626             .resources
627             .get_mut(&resource_id)
628             .ok_or(RutabagaError::InvalidResourceId)?;
629 
630         ctx.detach(&resource);
631         Ok(())
632     }
633 
634     /// Submits `commands` to the context given by `ctx_id`.
submit_command(&mut self, ctx_id: u32, commands: &mut [u8]) -> RutabagaResult<()>635     pub fn submit_command(&mut self, ctx_id: u32, commands: &mut [u8]) -> RutabagaResult<()> {
636         let ctx = self
637             .contexts
638             .get_mut(&ctx_id)
639             .ok_or(RutabagaError::InvalidContextId)?;
640 
641         ctx.submit_cmd(commands)
642     }
643 }
644 
645 /// Rutabaga Builder, following the Rust builder pattern.
646 pub struct RutabagaBuilder {
647     display_width: Option<u32>,
648     display_height: Option<u32>,
649     default_component: RutabagaComponentType,
650     virglrenderer_flags: Option<VirglRendererFlags>,
651     gfxstream_flags: Option<GfxstreamFlags>,
652     channels: Option<Vec<RutabagaChannel>>,
653 }
654 
655 impl RutabagaBuilder {
656     /// Create new a RutabagaBuilder.
new(default_component: RutabagaComponentType) -> RutabagaBuilder657     pub fn new(default_component: RutabagaComponentType) -> RutabagaBuilder {
658         RutabagaBuilder {
659             display_width: None,
660             display_height: None,
661             default_component,
662             virglrenderer_flags: None,
663             gfxstream_flags: None,
664             channels: None,
665         }
666     }
667 
668     /// Set display width for the RutabagaBuilder
set_display_width(mut self, display_width: u32) -> RutabagaBuilder669     pub fn set_display_width(mut self, display_width: u32) -> RutabagaBuilder {
670         self.display_width = Some(display_width);
671         self
672     }
673 
674     /// Set display height for the RutabagaBuilder
set_display_height(mut self, display_height: u32) -> RutabagaBuilder675     pub fn set_display_height(mut self, display_height: u32) -> RutabagaBuilder {
676         self.display_height = Some(display_height);
677         self
678     }
679 
680     /// Set virglrenderer flags for the RutabagaBuilder
set_virglrenderer_flags( mut self, virglrenderer_flags: VirglRendererFlags, ) -> RutabagaBuilder681     pub fn set_virglrenderer_flags(
682         mut self,
683         virglrenderer_flags: VirglRendererFlags,
684     ) -> RutabagaBuilder {
685         self.virglrenderer_flags = Some(virglrenderer_flags);
686         self
687     }
688 
689     /// Set gfxstream flags for the RutabagaBuilder
set_gfxstream_flags(mut self, gfxstream_flags: GfxstreamFlags) -> RutabagaBuilder690     pub fn set_gfxstream_flags(mut self, gfxstream_flags: GfxstreamFlags) -> RutabagaBuilder {
691         self.gfxstream_flags = Some(gfxstream_flags);
692         self
693     }
694 
695     /// Set rutabaga channels for the RutabagaBuilder
set_rutabaga_channels( mut self, channels: Option<Vec<RutabagaChannel>>, ) -> RutabagaBuilder696     pub fn set_rutabaga_channels(
697         mut self,
698         channels: Option<Vec<RutabagaChannel>>,
699     ) -> RutabagaBuilder {
700         self.channels = channels;
701         self
702     }
703 
704     /// Builds Rutabaga and returns a handle to it.
705     ///
706     /// This should be only called once per every virtual machine instance.  Rutabaga tries to
707     /// intialize all 3D components which have been built. In 2D mode, only the 2D component is
708     /// initialized.
build(self) -> RutabagaResult<Rutabaga>709     pub fn build(self) -> RutabagaResult<Rutabaga> {
710         let mut rutabaga_components: Map<RutabagaComponentType, Box<dyn RutabagaComponent>> =
711             Default::default();
712 
713         let mut rutabaga_capsets: Vec<RutabagaCapsetInfo> = Default::default();
714 
715         if self.default_component == RutabagaComponentType::Rutabaga2D {
716             let rutabaga_2d = Rutabaga2D::init()?;
717             rutabaga_components.insert(RutabagaComponentType::Rutabaga2D, rutabaga_2d);
718         } else {
719             #[cfg(feature = "virgl_renderer")]
720             if self.default_component == RutabagaComponentType::VirglRenderer {
721                 let virglrenderer_flags = self
722                     .virglrenderer_flags
723                     .ok_or(RutabagaError::InvalidRutabagaBuild)?;
724 
725                 let virgl = VirglRenderer::init(virglrenderer_flags)?;
726                 rutabaga_components.insert(RutabagaComponentType::VirglRenderer, virgl);
727 
728                 rutabaga_capsets.push(RutabagaCapsetInfo {
729                     capset_id: RUTABAGA_CAPSET_VIRGL,
730                     component: RutabagaComponentType::VirglRenderer,
731                 });
732                 rutabaga_capsets.push(RutabagaCapsetInfo {
733                     capset_id: RUTABAGA_CAPSET_VIRGL2,
734                     component: RutabagaComponentType::VirglRenderer,
735                 });
736                 rutabaga_capsets.push(RutabagaCapsetInfo {
737                     capset_id: RUTABAGA_CAPSET_VENUS,
738                     component: RutabagaComponentType::VirglRenderer,
739                 });
740             }
741 
742             #[cfg(feature = "gfxstream")]
743             if self.default_component == RutabagaComponentType::Gfxstream {
744                 let display_width = self
745                     .display_width
746                     .ok_or(RutabagaError::InvalidRutabagaBuild)?;
747                 let display_height = self
748                     .display_height
749                     .ok_or(RutabagaError::InvalidRutabagaBuild)?;
750 
751                 let gfxstream_flags = self
752                     .gfxstream_flags
753                     .ok_or(RutabagaError::InvalidRutabagaBuild)?;
754 
755                 let gfxstream = Gfxstream::init(display_width, display_height, gfxstream_flags)?;
756                 rutabaga_components.insert(RutabagaComponentType::Gfxstream, gfxstream);
757 
758                 rutabaga_capsets.push(RutabagaCapsetInfo {
759                     capset_id: RUTABAGA_CAPSET_GFXSTREAM,
760                     component: RutabagaComponentType::Gfxstream,
761                 });
762             }
763 
764             let cross_domain = CrossDomain::init(self.channels)?;
765             rutabaga_components.insert(RutabagaComponentType::CrossDomain, cross_domain);
766 
767             rutabaga_capsets.push(RutabagaCapsetInfo {
768                 capset_id: RUTABAGA_CAPSET_CROSS_DOMAIN,
769                 component: RutabagaComponentType::CrossDomain,
770             });
771         }
772 
773         Ok(Rutabaga {
774             components: rutabaga_components,
775             resources: Default::default(),
776             contexts: Default::default(),
777             default_component: self.default_component,
778             capset_info: rutabaga_capsets,
779         })
780     }
781 }
782