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