• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 // Copyright 2020 The ChromiumOS Authors
2 // Use of this source code is governed by a BSD-style license that can be
3 // found in the LICENSE file.
4 
5 //! rutabaga_core: Cross-platform, Rust-based, Wayland and Vulkan centric GPU virtualization.
6 
7 use std::collections::BTreeMap as Map;
8 use std::sync::Arc;
9 
10 use data_model::VolatileSlice;
11 
12 use crate::cross_domain::CrossDomain;
13 #[cfg(feature = "gfxstream")]
14 use crate::gfxstream::Gfxstream;
15 use crate::rutabaga_2d::Rutabaga2D;
16 use crate::rutabaga_os::SafeDescriptor;
17 use crate::rutabaga_utils::*;
18 #[cfg(feature = "virgl_renderer")]
19 use crate::virgl_renderer::VirglRenderer;
20 
21 const RUTABAGA_DEFAULT_WIDTH: u32 = 1280;
22 const RUTABAGA_DEFAULT_HEIGHT: u32 = 1024;
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     /// Bitmask of components that have already imported this resource
45     pub component_mask: u8,
46 }
47 
48 /// A RutabagaComponent is a building block of the Virtual Graphics Interface (VGI).  Each component
49 /// on it's own is sufficient to virtualize graphics on many Google products.  These components wrap
50 /// libraries like gfxstream or virglrenderer, and Rutabaga's own 2D and cross-domain prototype
51 /// functionality.
52 ///
53 /// Most methods return a `RutabagaResult` that indicate the success, failure, or requested data for
54 /// the given command.
55 pub trait RutabagaComponent {
56     /// Implementations should return the version and size of the given capset_id.  (0, 0) is
57     /// returned by default.
get_capset_info(&self, _capset_id: u32) -> (u32, u32)58     fn get_capset_info(&self, _capset_id: u32) -> (u32, u32) {
59         (0, 0)
60     }
61 
62     /// Implementations should return the capabilites of given a `capset_id` and `version`.  A
63     /// zero-sized array is returned by default.
get_capset(&self, _capset_id: u32, _version: u32) -> Vec<u8>64     fn get_capset(&self, _capset_id: u32, _version: u32) -> Vec<u8> {
65         Vec::new()
66     }
67 
68     /// Implementations should set their internal context to be the reserved context 0.
force_ctx_0(&self)69     fn force_ctx_0(&self) {}
70 
71     /// Implementations must create a fence that represents the completion of prior work.  This is
72     /// required for synchronization with the guest kernel.
create_fence(&mut self, _fence: RutabagaFence) -> RutabagaResult<()>73     fn create_fence(&mut self, _fence: RutabagaFence) -> RutabagaResult<()> {
74         Ok(())
75     }
76 
77     /// Used only by VirglRenderer to poll when its poll_descriptor is signaled.
event_poll(&self)78     fn event_poll(&self) {}
79 
80     /// Used only by VirglRenderer to return a poll_descriptor that is signaled when a poll() is
81     /// necessary.
poll_descriptor(&self) -> Option<SafeDescriptor>82     fn poll_descriptor(&self) -> Option<SafeDescriptor> {
83         None
84     }
85 
86     /// Implementations must create a resource with the given metadata.  For 2D rutabaga components,
87     /// this a system memory allocation.  For 3D components, this is typically a GL texture or
88     /// buffer.  Vulkan components should use blob resources instead.
create_3d( &self, resource_id: u32, _resource_create_3d: ResourceCreate3D, ) -> RutabagaResult<RutabagaResource>89     fn create_3d(
90         &self,
91         resource_id: u32,
92         _resource_create_3d: ResourceCreate3D,
93     ) -> RutabagaResult<RutabagaResource> {
94         Ok(RutabagaResource {
95             resource_id,
96             handle: None,
97             blob: false,
98             blob_mem: 0,
99             blob_flags: 0,
100             map_info: None,
101             info_2d: None,
102             info_3d: None,
103             vulkan_info: None,
104             backing_iovecs: None,
105             component_mask: 0,
106         })
107     }
108 
109     /// Implementations must attach `vecs` to the resource.
attach_backing( &self, _resource_id: u32, _vecs: &mut Vec<RutabagaIovec>, ) -> RutabagaResult<()>110     fn attach_backing(
111         &self,
112         _resource_id: u32,
113         _vecs: &mut Vec<RutabagaIovec>,
114     ) -> RutabagaResult<()> {
115         Ok(())
116     }
117 
118     /// Implementations must detach `vecs` from the resource.
detach_backing(&self, _resource_id: u32)119     fn detach_backing(&self, _resource_id: u32) {}
120 
121     /// Implementations must release the guest kernel reference on the resource.
unref_resource(&self, _resource_id: u32)122     fn unref_resource(&self, _resource_id: u32) {}
123 
124     /// Implementations must perform the transfer write operation.  For 2D rutabaga components, this
125     /// done via memcpy().  For 3D components, this is typically done via glTexSubImage(..).
transfer_write( &self, _ctx_id: u32, _resource: &mut RutabagaResource, _transfer: Transfer3D, ) -> RutabagaResult<()>126     fn transfer_write(
127         &self,
128         _ctx_id: u32,
129         _resource: &mut RutabagaResource,
130         _transfer: Transfer3D,
131     ) -> RutabagaResult<()> {
132         Ok(())
133     }
134 
135     /// Implementations must perform the transfer read operation.  For 2D rutabaga components, this
136     /// 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<()>137     fn transfer_read(
138         &self,
139         _ctx_id: u32,
140         _resource: &mut RutabagaResource,
141         _transfer: Transfer3D,
142         _buf: Option<VolatileSlice>,
143     ) -> RutabagaResult<()> {
144         Ok(())
145     }
146 
147     /// Implementations must flush the given resource to the display.
resource_flush(&self, _resource_id: &mut RutabagaResource) -> RutabagaResult<()>148     fn resource_flush(&self, _resource_id: &mut RutabagaResource) -> RutabagaResult<()> {
149         Err(RutabagaError::Unsupported)
150     }
151 
152     /// Implementations must create a blob resource on success.  The memory parameters, size, and
153     /// 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>154     fn create_blob(
155         &mut self,
156         _ctx_id: u32,
157         _resource_id: u32,
158         _resource_create_blob: ResourceCreateBlob,
159         _iovec_opt: Option<Vec<RutabagaIovec>>,
160         _handle_opt: Option<RutabagaHandle>,
161     ) -> RutabagaResult<RutabagaResource> {
162         Err(RutabagaError::Unsupported)
163     }
164 
165     /// Implementations must map the blob resource on success.  This is typically done by
166     /// glMapBufferRange(...) or vkMapMemory.
map(&self, _resource_id: u32) -> RutabagaResult<RutabagaMapping>167     fn map(&self, _resource_id: u32) -> RutabagaResult<RutabagaMapping> {
168         Err(RutabagaError::Unsupported)
169     }
170 
171     /// Implementations must unmap the blob resource on success.  This is typically done by
172     /// glUnmapBuffer(...) or vkUnmapMemory.
unmap(&self, _resource_id: u32) -> RutabagaResult<()>173     fn unmap(&self, _resource_id: u32) -> RutabagaResult<()> {
174         Err(RutabagaError::Unsupported)
175     }
176 
177     /// Implementations must return a RutabagaHandle of the fence on success.
export_fence(&self, _fence_id: u32) -> RutabagaResult<RutabagaHandle>178     fn export_fence(&self, _fence_id: u32) -> RutabagaResult<RutabagaHandle> {
179         Err(RutabagaError::Unsupported)
180     }
181 
182     /// Implementations must create a context for submitting commands.  The command stream of the
183     /// context is determined by `context_init`.  For virgl contexts, it is a Gallium/TGSI command
184     /// 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>>185     fn create_context(
186         &self,
187         _ctx_id: u32,
188         _context_init: u32,
189         _context_name: Option<&str>,
190         _fence_handler: RutabagaFenceHandler,
191     ) -> RutabagaResult<Box<dyn RutabagaContext>> {
192         Err(RutabagaError::Unsupported)
193     }
194 }
195 
196 pub trait RutabagaContext {
197     /// 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>198     fn context_create_blob(
199         &mut self,
200         _resource_id: u32,
201         _resource_create_blob: ResourceCreateBlob,
202         _handle_opt: Option<RutabagaHandle>,
203     ) -> RutabagaResult<RutabagaResource> {
204         Err(RutabagaError::Unsupported)
205     }
206 
207     /// Implementations must handle the context-specific command stream.
submit_cmd(&mut self, _commands: &mut [u8]) -> RutabagaResult<()>208     fn submit_cmd(&mut self, _commands: &mut [u8]) -> RutabagaResult<()>;
209 
210     /// Implementations may use `resource` in this context's command stream.
attach(&mut self, _resource: &mut RutabagaResource)211     fn attach(&mut self, _resource: &mut RutabagaResource);
212 
213     /// Implementations must stop using `resource` in this context's command stream.
detach(&mut self, _resource: &RutabagaResource)214     fn detach(&mut self, _resource: &RutabagaResource);
215 
216     /// Implementations must create a fence on specified `ring_idx` in `fence`.  This
217     /// allows for multiple synchronizations timelines per RutabagaContext.
context_create_fence(&mut self, _fence: RutabagaFence) -> RutabagaResult<()>218     fn context_create_fence(&mut self, _fence: RutabagaFence) -> RutabagaResult<()> {
219         Err(RutabagaError::Unsupported)
220     }
221 
222     /// Implementations must return the component type associated with the context.
component_type(&self) -> RutabagaComponentType223     fn component_type(&self) -> RutabagaComponentType;
224 }
225 
226 #[derive(Copy, Clone)]
227 struct RutabagaCapsetInfo {
228     pub capset_id: u32,
229     pub component: RutabagaComponentType,
230     pub name: &'static str,
231 }
232 
233 const RUTABAGA_CAPSETS: [RutabagaCapsetInfo; 6] = [
234     RutabagaCapsetInfo {
235         capset_id: RUTABAGA_CAPSET_VIRGL,
236         component: RutabagaComponentType::VirglRenderer,
237         name: "virgl",
238     },
239     RutabagaCapsetInfo {
240         capset_id: RUTABAGA_CAPSET_VIRGL2,
241         component: RutabagaComponentType::VirglRenderer,
242         name: "virgl2",
243     },
244     RutabagaCapsetInfo {
245         capset_id: RUTABAGA_CAPSET_GFXSTREAM,
246         component: RutabagaComponentType::Gfxstream,
247         name: "gfxstream",
248     },
249     RutabagaCapsetInfo {
250         capset_id: RUTABAGA_CAPSET_VENUS,
251         component: RutabagaComponentType::VirglRenderer,
252         name: "venus",
253     },
254     RutabagaCapsetInfo {
255         capset_id: RUTABAGA_CAPSET_CROSS_DOMAIN,
256         component: RutabagaComponentType::CrossDomain,
257         name: "cross-domain",
258     },
259     RutabagaCapsetInfo {
260         capset_id: RUTABAGA_CAPSET_DRM,
261         component: RutabagaComponentType::VirglRenderer,
262         name: "drm",
263     },
264 ];
265 
calculate_capset_mask(context_names: Vec<String>) -> u64266 pub fn calculate_capset_mask(context_names: Vec<String>) -> u64 {
267     let mut capset_mask = 0;
268     context_names.into_iter().for_each(|name| {
269         if let Some(capset) = RUTABAGA_CAPSETS.iter().find(|capset| capset.name == name) {
270             capset_mask |= 1 << capset.capset_id;
271         };
272     });
273 
274     capset_mask
275 }
276 
calculate_capset_names(capset_mask: u64) -> Vec<String>277 pub fn calculate_capset_names(capset_mask: u64) -> Vec<String> {
278     RUTABAGA_CAPSETS
279         .iter()
280         .filter(|capset| capset_mask & (1 << capset.capset_id) != 0)
281         .map(|capset| capset.name.to_string())
282         .collect()
283 }
284 
285 /// The global libary handle used to query capability sets, create resources and contexts.
286 ///
287 /// Currently, Rutabaga only supports one default component.  Many components running at the
288 /// same time is a stretch goal of Rutabaga GFX.
289 ///
290 /// Not thread-safe, but can be made so easily.  Making non-Rutabaga, C/C++ components
291 /// thread-safe is more difficult.
292 pub struct Rutabaga {
293     resources: Map<u32, RutabagaResource>,
294     contexts: Map<u32, Box<dyn RutabagaContext>>,
295     // Declare components after resources and contexts such that it is dropped last.
296     components: Map<RutabagaComponentType, Box<dyn RutabagaComponent>>,
297     default_component: RutabagaComponentType,
298     capset_info: Vec<RutabagaCapsetInfo>,
299     fence_handler: RutabagaFenceHandler,
300 }
301 
302 impl Rutabaga {
capset_id_to_component_type(&self, capset_id: u32) -> RutabagaResult<RutabagaComponentType>303     fn capset_id_to_component_type(&self, capset_id: u32) -> RutabagaResult<RutabagaComponentType> {
304         let component = self
305             .capset_info
306             .iter()
307             .find(|capset_info| capset_info.capset_id == capset_id)
308             .ok_or(RutabagaError::InvalidCapset)?
309             .component;
310 
311         Ok(component)
312     }
313 
capset_index_to_component_info(&self, index: u32) -> RutabagaResult<RutabagaCapsetInfo>314     fn capset_index_to_component_info(&self, index: u32) -> RutabagaResult<RutabagaCapsetInfo> {
315         let idx = index as usize;
316         if idx >= self.capset_info.len() {
317             return Err(RutabagaError::InvalidCapset);
318         }
319 
320         Ok(self.capset_info[idx])
321     }
322 
323     /// Gets the version and size for the capabilty set `index`.
get_capset_info(&self, index: u32) -> RutabagaResult<(u32, u32, u32)>324     pub fn get_capset_info(&self, index: u32) -> RutabagaResult<(u32, u32, u32)> {
325         let capset_info = self.capset_index_to_component_info(index)?;
326 
327         let component = self
328             .components
329             .get(&capset_info.component)
330             .ok_or(RutabagaError::InvalidComponent)?;
331 
332         let (capset_version, capset_size) = component.get_capset_info(capset_info.capset_id);
333         Ok((capset_info.capset_id, capset_version, capset_size))
334     }
335 
336     /// Gets the capability set for the `capset_id` and `version`.
337     /// Each capability set is associated with a context type, which is associated
338     /// with a rutabaga component.
get_capset(&self, capset_id: u32, version: u32) -> RutabagaResult<Vec<u8>>339     pub fn get_capset(&self, capset_id: u32, version: u32) -> RutabagaResult<Vec<u8>> {
340         // The default workaround is just until context types are fully supported in all
341         // Google kernels.
342         let component_type = self
343             .capset_id_to_component_type(capset_id)
344             .unwrap_or(self.default_component);
345 
346         let component = self
347             .components
348             .get(&component_type)
349             .ok_or(RutabagaError::InvalidComponent)?;
350 
351         Ok(component.get_capset(capset_id, version))
352     }
353 
354     /// Gets the number of capsets
get_num_capsets(&self) -> u32355     pub fn get_num_capsets(&self) -> u32 {
356         self.capset_info.len() as u32
357     }
358 
359     /// Forces context zero for the default rutabaga component.
force_ctx_0(&self)360     pub fn force_ctx_0(&self) {
361         if let Some(component) = self.components.get(&self.default_component) {
362             component.force_ctx_0();
363         }
364     }
365 
366     /// Creates a fence with the given `fence`.
367     /// If the flags include RUTABAGA_FLAG_INFO_RING_IDX, then the fence is created on a
368     /// specific timeline on the specific context.
create_fence(&mut self, fence: RutabagaFence) -> RutabagaResult<()>369     pub fn create_fence(&mut self, fence: RutabagaFence) -> RutabagaResult<()> {
370         if fence.flags & RUTABAGA_FLAG_INFO_RING_IDX != 0 {
371             let ctx = self
372                 .contexts
373                 .get_mut(&fence.ctx_id)
374                 .ok_or(RutabagaError::InvalidContextId)?;
375 
376             ctx.context_create_fence(fence)?;
377         } else {
378             let component = self
379                 .components
380                 .get_mut(&self.default_component)
381                 .ok_or(RutabagaError::InvalidComponent)?;
382 
383             component.create_fence(fence)?;
384         }
385 
386         Ok(())
387     }
388 
389     /// Polls the default rutabaga component.
event_poll(&self)390     pub fn event_poll(&self) {
391         if let Some(component) = self.components.get(&self.default_component) {
392             component.event_poll();
393         }
394     }
395 
396     /// Returns a pollable descriptor for the default rutabaga component. In practice, it is only
397     /// not None if the default component is virglrenderer.
poll_descriptor(&self) -> Option<SafeDescriptor>398     pub fn poll_descriptor(&self) -> Option<SafeDescriptor> {
399         let component = self.components.get(&self.default_component).or(None)?;
400         component.poll_descriptor()
401     }
402 
403     /// Creates a resource with the `resource_create_3d` metadata.
resource_create_3d( &mut self, resource_id: u32, resource_create_3d: ResourceCreate3D, ) -> RutabagaResult<()>404     pub fn resource_create_3d(
405         &mut self,
406         resource_id: u32,
407         resource_create_3d: ResourceCreate3D,
408     ) -> RutabagaResult<()> {
409         let component = self
410             .components
411             .get_mut(&self.default_component)
412             .ok_or(RutabagaError::InvalidComponent)?;
413 
414         if self.resources.contains_key(&resource_id) {
415             return Err(RutabagaError::InvalidResourceId);
416         }
417 
418         let resource = component.create_3d(resource_id, resource_create_3d)?;
419         self.resources.insert(resource_id, resource);
420         Ok(())
421     }
422 
423     /// Attaches `vecs` to the resource.
attach_backing( &mut self, resource_id: u32, mut vecs: Vec<RutabagaIovec>, ) -> RutabagaResult<()>424     pub fn attach_backing(
425         &mut self,
426         resource_id: u32,
427         mut vecs: Vec<RutabagaIovec>,
428     ) -> RutabagaResult<()> {
429         let component = self
430             .components
431             .get_mut(&self.default_component)
432             .ok_or(RutabagaError::InvalidComponent)?;
433 
434         let mut resource = self
435             .resources
436             .get_mut(&resource_id)
437             .ok_or(RutabagaError::InvalidResourceId)?;
438 
439         component.attach_backing(resource_id, &mut vecs)?;
440         resource.backing_iovecs = Some(vecs);
441         Ok(())
442     }
443 
444     /// Detaches any previously attached iovecs from the resource.
detach_backing(&mut self, resource_id: u32) -> RutabagaResult<()>445     pub fn detach_backing(&mut self, resource_id: u32) -> RutabagaResult<()> {
446         let component = self
447             .components
448             .get_mut(&self.default_component)
449             .ok_or(RutabagaError::InvalidComponent)?;
450 
451         let resource = self
452             .resources
453             .get_mut(&resource_id)
454             .ok_or(RutabagaError::InvalidResourceId)?;
455 
456         component.detach_backing(resource_id);
457         resource.backing_iovecs = None;
458         Ok(())
459     }
460 
461     /// Releases guest kernel reference on the resource.
unref_resource(&mut self, resource_id: u32) -> RutabagaResult<()>462     pub fn unref_resource(&mut self, resource_id: u32) -> RutabagaResult<()> {
463         let component = self
464             .components
465             .get_mut(&self.default_component)
466             .ok_or(RutabagaError::InvalidComponent)?;
467 
468         self.resources
469             .remove(&resource_id)
470             .ok_or(RutabagaError::InvalidResourceId)?;
471 
472         component.unref_resource(resource_id);
473         Ok(())
474     }
475 
476     /// For HOST3D_GUEST resources, copies from the attached iovecs to the host resource.  For
477     /// 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<()>478     pub fn transfer_write(
479         &mut self,
480         ctx_id: u32,
481         resource_id: u32,
482         transfer: Transfer3D,
483     ) -> RutabagaResult<()> {
484         let component = self
485             .components
486             .get(&self.default_component)
487             .ok_or(RutabagaError::InvalidComponent)?;
488 
489         let resource = self
490             .resources
491             .get_mut(&resource_id)
492             .ok_or(RutabagaError::InvalidResourceId)?;
493 
494         component.transfer_write(ctx_id, resource, transfer)
495     }
496 
497     /// 1) If specified, copies to `buf` from the host resource.
498     /// 2) Otherwise, for HOST3D_GUEST resources, copies to the attached iovecs from the host
499     ///    resource.  For HOST3D resources, this may invalidate caches, though this feature is
500     ///    unused by guest userspace.
transfer_read( &mut self, ctx_id: u32, resource_id: u32, transfer: Transfer3D, buf: Option<VolatileSlice>, ) -> RutabagaResult<()>501     pub fn transfer_read(
502         &mut self,
503         ctx_id: u32,
504         resource_id: u32,
505         transfer: Transfer3D,
506         buf: Option<VolatileSlice>,
507     ) -> RutabagaResult<()> {
508         let component = self
509             .components
510             .get(&self.default_component)
511             .ok_or(RutabagaError::InvalidComponent)?;
512 
513         let resource = self
514             .resources
515             .get_mut(&resource_id)
516             .ok_or(RutabagaError::InvalidResourceId)?;
517 
518         component.transfer_read(ctx_id, resource, transfer, buf)
519     }
520 
resource_flush(&mut self, resource_id: u32) -> RutabagaResult<()>521     pub fn resource_flush(&mut self, resource_id: u32) -> RutabagaResult<()> {
522         let component = self
523             .components
524             .get(&self.default_component)
525             .ok_or(RutabagaError::Unsupported)?;
526 
527         let resource = self
528             .resources
529             .get_mut(&resource_id)
530             .ok_or(RutabagaError::InvalidResourceId)?;
531 
532         component.resource_flush(resource)
533     }
534 
535     /// Creates a blob resource with the `ctx_id` and `resource_create_blob` metadata.
536     /// Associates `iovecs` with the resource, if there are any.  Associates externally
537     /// 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<()>538     pub fn resource_create_blob(
539         &mut self,
540         ctx_id: u32,
541         resource_id: u32,
542         resource_create_blob: ResourceCreateBlob,
543         iovecs: Option<Vec<RutabagaIovec>>,
544         handle: Option<RutabagaHandle>,
545     ) -> RutabagaResult<()> {
546         if self.resources.contains_key(&resource_id) {
547             return Err(RutabagaError::InvalidResourceId);
548         }
549 
550         let component = self
551             .components
552             .get_mut(&self.default_component)
553             .ok_or(RutabagaError::InvalidComponent)?;
554 
555         let mut context = None;
556         // For the cross-domain context, we'll need to create the blob resource via a home-grown
557         // rutabaga context rather than one from an external C/C++ component.  Use `ctx_id` and
558         // the component type if it happens to be a cross-domain context.
559         if ctx_id > 0 {
560             let ctx = self
561                 .contexts
562                 .get_mut(&ctx_id)
563                 .ok_or(RutabagaError::InvalidContextId)?;
564 
565             if ctx.component_type() == RutabagaComponentType::CrossDomain {
566                 context = Some(ctx);
567             }
568         }
569 
570         let resource = match context {
571             Some(ctx) => ctx.context_create_blob(resource_id, resource_create_blob, handle)?,
572             None => {
573                 component.create_blob(ctx_id, resource_id, resource_create_blob, iovecs, handle)?
574             }
575         };
576 
577         self.resources.insert(resource_id, resource);
578         Ok(())
579     }
580 
581     /// Returns a memory mapping of the blob resource.
map(&self, resource_id: u32) -> RutabagaResult<RutabagaMapping>582     pub fn map(&self, resource_id: u32) -> RutabagaResult<RutabagaMapping> {
583         let component = self
584             .components
585             .get(&self.default_component)
586             .ok_or(RutabagaError::InvalidComponent)?;
587 
588         if !self.resources.contains_key(&resource_id) {
589             return Err(RutabagaError::InvalidResourceId);
590         }
591 
592         component.map(resource_id)
593     }
594 
595     /// Unmaps the blob resource from the default component
unmap(&self, resource_id: u32) -> RutabagaResult<()>596     pub fn unmap(&self, resource_id: u32) -> RutabagaResult<()> {
597         let component = self
598             .components
599             .get(&self.default_component)
600             .ok_or(RutabagaError::InvalidComponent)?;
601 
602         if !self.resources.contains_key(&resource_id) {
603             return Err(RutabagaError::InvalidResourceId);
604         }
605 
606         component.unmap(resource_id)
607     }
608 
609     /// Returns the `map_info` of the blob resource. The valid values for `map_info`
610     /// are defined in the virtio-gpu spec.
map_info(&self, resource_id: u32) -> RutabagaResult<u32>611     pub fn map_info(&self, resource_id: u32) -> RutabagaResult<u32> {
612         let resource = self
613             .resources
614             .get(&resource_id)
615             .ok_or(RutabagaError::InvalidResourceId)?;
616 
617         resource
618             .map_info
619             .ok_or(RutabagaError::SpecViolation("no map info available"))
620     }
621 
622     /// Returns the `vulkan_info` of the blob resource, which consists of the physical device
623     /// index and memory index associated with the resource.
vulkan_info(&self, resource_id: u32) -> RutabagaResult<VulkanInfo>624     pub fn vulkan_info(&self, resource_id: u32) -> RutabagaResult<VulkanInfo> {
625         let resource = self
626             .resources
627             .get(&resource_id)
628             .ok_or(RutabagaError::InvalidResourceId)?;
629 
630         resource.vulkan_info.ok_or(RutabagaError::InvalidVulkanInfo)
631     }
632 
633     /// Returns the 3D info associated with the resource, if any.
query(&self, resource_id: u32) -> RutabagaResult<Resource3DInfo>634     pub fn query(&self, resource_id: u32) -> RutabagaResult<Resource3DInfo> {
635         let resource = self
636             .resources
637             .get(&resource_id)
638             .ok_or(RutabagaError::InvalidResourceId)?;
639 
640         resource
641             .info_3d
642             .ok_or(RutabagaError::SpecViolation("no 3d info available"))
643     }
644 
645     /// Exports a blob resource.  See virtio-gpu spec for blob flag use flags.
export_blob(&mut self, resource_id: u32) -> RutabagaResult<RutabagaHandle>646     pub fn export_blob(&mut self, resource_id: u32) -> RutabagaResult<RutabagaHandle> {
647         let resource = self
648             .resources
649             .get_mut(&resource_id)
650             .ok_or(RutabagaError::InvalidResourceId)?;
651 
652         // We can inspect blob flags only once guest minigbm is fully transitioned to blob.
653         let share_mask = RUTABAGA_BLOB_FLAG_USE_SHAREABLE | RUTABAGA_BLOB_FLAG_USE_CROSS_DEVICE;
654         let shareable = (resource.blob_flags & share_mask != 0) || !resource.blob;
655 
656         let opt = resource.handle.take();
657 
658         match (opt, shareable) {
659             (Some(handle), true) => {
660                 let clone = handle.try_clone()?;
661                 resource.handle = Some(handle);
662                 Ok(clone)
663             }
664             (Some(handle), false) => {
665                 // Exactly one strong reference in this case.
666                 let hnd =
667                     Arc::try_unwrap(handle).map_err(|_| RutabagaError::InvalidRutabagaHandle)?;
668                 Ok(hnd)
669             }
670             _ => Err(RutabagaError::InvalidRutabagaHandle),
671         }
672     }
673 
674     /// Exports the given fence for import into other processes.
export_fence(&self, fence_id: u32) -> RutabagaResult<RutabagaHandle>675     pub fn export_fence(&self, fence_id: u32) -> RutabagaResult<RutabagaHandle> {
676         let component = self
677             .components
678             .get(&self.default_component)
679             .ok_or(RutabagaError::InvalidComponent)?;
680 
681         component.export_fence(fence_id)
682     }
683 
684     /// Creates a context with the given `ctx_id` and `context_init` variable.
685     /// `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<()>686     pub fn create_context(
687         &mut self,
688         ctx_id: u32,
689         context_init: u32,
690         context_name: Option<&str>,
691     ) -> RutabagaResult<()> {
692         // The default workaround is just until context types are fully supported in all
693         // Google kernels.
694         let capset_id = context_init & RUTABAGA_CONTEXT_INIT_CAPSET_ID_MASK;
695         let component_type = self
696             .capset_id_to_component_type(capset_id)
697             .unwrap_or(self.default_component);
698 
699         let component = self
700             .components
701             .get_mut(&component_type)
702             .ok_or(RutabagaError::InvalidComponent)?;
703 
704         if self.contexts.contains_key(&ctx_id) {
705             return Err(RutabagaError::InvalidContextId);
706         }
707 
708         let ctx = component.create_context(
709             ctx_id,
710             context_init,
711             context_name,
712             self.fence_handler.clone(),
713         )?;
714         self.contexts.insert(ctx_id, ctx);
715         Ok(())
716     }
717 
718     /// Destroys the context given by `ctx_id`.
destroy_context(&mut self, ctx_id: u32) -> RutabagaResult<()>719     pub fn destroy_context(&mut self, ctx_id: u32) -> RutabagaResult<()> {
720         self.contexts
721             .remove(&ctx_id)
722             .ok_or(RutabagaError::InvalidContextId)?;
723         Ok(())
724     }
725 
726     /// 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<()>727     pub fn context_attach_resource(&mut self, ctx_id: u32, resource_id: u32) -> RutabagaResult<()> {
728         let ctx = self
729             .contexts
730             .get_mut(&ctx_id)
731             .ok_or(RutabagaError::InvalidContextId)?;
732 
733         let resource = self
734             .resources
735             .get_mut(&resource_id)
736             .ok_or(RutabagaError::InvalidResourceId)?;
737 
738         ctx.attach(resource);
739         Ok(())
740     }
741 
742     /// 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<()>743     pub fn context_detach_resource(&mut self, ctx_id: u32, resource_id: u32) -> RutabagaResult<()> {
744         let ctx = self
745             .contexts
746             .get_mut(&ctx_id)
747             .ok_or(RutabagaError::InvalidContextId)?;
748 
749         let resource = self
750             .resources
751             .get_mut(&resource_id)
752             .ok_or(RutabagaError::InvalidResourceId)?;
753 
754         ctx.detach(resource);
755         Ok(())
756     }
757 
758     /// Submits `commands` to the context given by `ctx_id`.
submit_command(&mut self, ctx_id: u32, commands: &mut [u8]) -> RutabagaResult<()>759     pub fn submit_command(&mut self, ctx_id: u32, commands: &mut [u8]) -> RutabagaResult<()> {
760         let ctx = self
761             .contexts
762             .get_mut(&ctx_id)
763             .ok_or(RutabagaError::InvalidContextId)?;
764 
765         ctx.submit_cmd(commands)
766     }
767 }
768 
769 /// Rutabaga Builder, following the Rust builder pattern.
770 pub struct RutabagaBuilder {
771     display_width: u32,
772     display_height: u32,
773     default_component: RutabagaComponentType,
774     gfxstream_flags: GfxstreamFlags,
775     virglrenderer_flags: VirglRendererFlags,
776     capset_mask: u64,
777     channels: Option<Vec<RutabagaChannel>>,
778 }
779 
780 impl RutabagaBuilder {
781     /// Create new a RutabagaBuilder.
new(default_component: RutabagaComponentType, capset_mask: u64) -> RutabagaBuilder782     pub fn new(default_component: RutabagaComponentType, capset_mask: u64) -> RutabagaBuilder {
783         let virglrenderer_flags = VirglRendererFlags::new()
784             .use_thread_sync(true)
785             .use_async_fence_cb(true);
786         let gfxstream_flags = GfxstreamFlags::new().use_async_fence_cb(true);
787 
788         RutabagaBuilder {
789             display_width: RUTABAGA_DEFAULT_WIDTH,
790             display_height: RUTABAGA_DEFAULT_HEIGHT,
791             default_component,
792             gfxstream_flags,
793             virglrenderer_flags,
794             capset_mask,
795             channels: None,
796         }
797     }
798 
799     /// Set display width for the RutabagaBuilder
set_display_width(mut self, display_width: u32) -> RutabagaBuilder800     pub fn set_display_width(mut self, display_width: u32) -> RutabagaBuilder {
801         self.display_width = display_width;
802         self
803     }
804 
805     /// Set display height for the RutabagaBuilder
set_display_height(mut self, display_height: u32) -> RutabagaBuilder806     pub fn set_display_height(mut self, display_height: u32) -> RutabagaBuilder {
807         self.display_height = display_height;
808         self
809     }
810 
811     /// Sets use EGL flags in gfxstream + virglrenderer.
set_use_egl(mut self, v: bool) -> RutabagaBuilder812     pub fn set_use_egl(mut self, v: bool) -> RutabagaBuilder {
813         self.gfxstream_flags = self.gfxstream_flags.use_egl(v);
814         self.virglrenderer_flags = self.virglrenderer_flags.use_egl(v);
815         self
816     }
817 
818     /// Sets use GLES in gfxstream + virglrenderer.
set_use_gles(mut self, v: bool) -> RutabagaBuilder819     pub fn set_use_gles(mut self, v: bool) -> RutabagaBuilder {
820         self.gfxstream_flags = self.gfxstream_flags.use_gles(v);
821         self.virglrenderer_flags = self.virglrenderer_flags.use_gles(v);
822         self
823     }
824 
825     /// Sets use GLX flags in gfxstream + virglrenderer.
set_use_glx(mut self, v: bool) -> RutabagaBuilder826     pub fn set_use_glx(mut self, v: bool) -> RutabagaBuilder {
827         self.gfxstream_flags = self.gfxstream_flags.use_glx(v);
828         self.virglrenderer_flags = self.virglrenderer_flags.use_glx(v);
829         self
830     }
831 
832     /// Sets use surfaceless flags in gfxstream + virglrenderer.
set_use_surfaceless(mut self, v: bool) -> RutabagaBuilder833     pub fn set_use_surfaceless(mut self, v: bool) -> RutabagaBuilder {
834         self.gfxstream_flags = self.gfxstream_flags.use_surfaceless(v);
835         self.virglrenderer_flags = self.virglrenderer_flags.use_surfaceless(v);
836         self
837     }
838 
839     /// Sets use Vulkan in gfxstream + virglrenderer.
set_use_vulkan(mut self, v: bool) -> RutabagaBuilder840     pub fn set_use_vulkan(mut self, v: bool) -> RutabagaBuilder {
841         self.gfxstream_flags = self.gfxstream_flags.use_vulkan(v);
842         self.virglrenderer_flags = self.virglrenderer_flags.use_venus(v);
843         self
844     }
845 
846     /// Set use guest ANGLE in gfxstream
set_use_guest_angle(mut self, v: bool) -> RutabagaBuilder847     pub fn set_use_guest_angle(mut self, v: bool) -> RutabagaBuilder {
848         self.gfxstream_flags = self.gfxstream_flags.use_guest_angle(v);
849         self
850     }
851 
852     /// Set enable GLES 3.1 support in gfxstream
set_support_gles31(mut self, v: bool) -> RutabagaBuilder853     pub fn set_support_gles31(mut self, v: bool) -> RutabagaBuilder {
854         self.gfxstream_flags = self.gfxstream_flags.support_gles31(v);
855         self
856     }
857 
858     /// Sets use external blob in gfxstream + virglrenderer.
set_use_external_blob(mut self, v: bool) -> RutabagaBuilder859     pub fn set_use_external_blob(mut self, v: bool) -> RutabagaBuilder {
860         self.gfxstream_flags = self.gfxstream_flags.use_external_blob(v);
861         self.virglrenderer_flags = self.virglrenderer_flags.use_external_blob(v);
862         self
863     }
864 
865     /// Sets use system blob in gfxstream.
set_use_system_blob(mut self, v: bool) -> RutabagaBuilder866     pub fn set_use_system_blob(mut self, v: bool) -> RutabagaBuilder {
867         self.gfxstream_flags = self.gfxstream_flags.use_system_blob(v);
868         self
869     }
870 
871     /// Sets use render server in virglrenderer.
set_use_render_server(mut self, v: bool) -> RutabagaBuilder872     pub fn set_use_render_server(mut self, v: bool) -> RutabagaBuilder {
873         self.virglrenderer_flags = self.virglrenderer_flags.use_render_server(v);
874         self
875     }
876 
877     /// Use the Vulkan swapchain to draw on the host window for gfxstream.
set_wsi(mut self, v: Option<&RutabagaWsi>) -> RutabagaBuilder878     pub fn set_wsi(mut self, v: Option<&RutabagaWsi>) -> RutabagaBuilder {
879         self.gfxstream_flags = self.gfxstream_flags.set_wsi(v);
880         self
881     }
882 
883     /// Set rutabaga channels for the RutabagaBuilder
set_rutabaga_channels( mut self, channels: Option<Vec<RutabagaChannel>>, ) -> RutabagaBuilder884     pub fn set_rutabaga_channels(
885         mut self,
886         channels: Option<Vec<RutabagaChannel>>,
887     ) -> RutabagaBuilder {
888         self.channels = channels;
889         self
890     }
891 
892     /// Builds Rutabaga and returns a handle to it.
893     ///
894     /// This should be only called once per every virtual machine instance.  Rutabaga tries to
895     /// intialize all 3D components which have been built. In 2D mode, only the 2D component is
896     /// initialized.
build( mut self, fence_handler: RutabagaFenceHandler, #[allow(unused_variables)] rutabaga_server_descriptor: Option<SafeDescriptor>, ) -> RutabagaResult<Rutabaga>897     pub fn build(
898         mut self,
899         fence_handler: RutabagaFenceHandler,
900         #[allow(unused_variables)] rutabaga_server_descriptor: Option<SafeDescriptor>,
901     ) -> RutabagaResult<Rutabaga> {
902         let mut rutabaga_components: Map<RutabagaComponentType, Box<dyn RutabagaComponent>> =
903             Default::default();
904 
905         #[allow(unused_mut)]
906         let mut rutabaga_capsets: Vec<RutabagaCapsetInfo> = Default::default();
907 
908         let capset_enabled =
909             |capset_id: u32| -> bool { (self.capset_mask & (1 << capset_id)) != 0 };
910 
911         let mut push_capset = |capset_id: u32| {
912             if let Some(capset) = RUTABAGA_CAPSETS
913                 .iter()
914                 .find(|capset| capset_id == capset.capset_id)
915             {
916                 if self.capset_mask != 0 {
917                     if capset_enabled(capset.capset_id) {
918                         rutabaga_capsets.push(*capset);
919                     }
920                 } else {
921                     // Unconditionally push capset -- this should eventually be deleted when context types are
922                     // always specified by crosvm launchers.
923                     rutabaga_capsets.push(*capset);
924                 }
925             };
926         };
927 
928         if self.capset_mask != 0 {
929             let supports_gfxstream = capset_enabled(RUTABAGA_CAPSET_GFXSTREAM);
930             let supports_virglrenderer = capset_enabled(RUTABAGA_CAPSET_VIRGL2)
931                 | capset_enabled(RUTABAGA_CAPSET_VENUS)
932                 | capset_enabled(RUTABAGA_CAPSET_DRM);
933 
934             if supports_gfxstream {
935                 self.default_component = RutabagaComponentType::Gfxstream;
936             } else if supports_virglrenderer {
937                 self.default_component = RutabagaComponentType::VirglRenderer;
938             } else {
939                 self.default_component = RutabagaComponentType::CrossDomain;
940             }
941 
942             self.virglrenderer_flags = self
943                 .virglrenderer_flags
944                 .use_virgl(capset_enabled(RUTABAGA_CAPSET_VIRGL2))
945                 .use_venus(capset_enabled(RUTABAGA_CAPSET_VENUS))
946                 .use_drm(capset_enabled(RUTABAGA_CAPSET_DRM));
947         }
948 
949         // Make sure that disabled components are not used as default.
950         #[cfg(not(feature = "virgl_renderer"))]
951         if self.default_component == RutabagaComponentType::VirglRenderer {
952             return Err(RutabagaError::InvalidRutabagaBuild(
953                 "virgl renderer feature not enabled",
954             ));
955         }
956         #[cfg(not(feature = "gfxstream"))]
957         if self.default_component == RutabagaComponentType::Gfxstream {
958             return Err(RutabagaError::InvalidRutabagaBuild(
959                 "gfxstream feature not enabled",
960             ));
961         }
962 
963         if self.default_component == RutabagaComponentType::Rutabaga2D {
964             let rutabaga_2d = Rutabaga2D::init(fence_handler.clone())?;
965             rutabaga_components.insert(RutabagaComponentType::Rutabaga2D, rutabaga_2d);
966         } else {
967             #[cfg(feature = "virgl_renderer")]
968             if self.default_component == RutabagaComponentType::VirglRenderer {
969                 #[cfg(not(feature = "virgl_renderer_next"))]
970                 let rutabaga_server_descriptor = None;
971 
972                 let virgl = VirglRenderer::init(
973                     self.virglrenderer_flags,
974                     fence_handler.clone(),
975                     rutabaga_server_descriptor,
976                 )?;
977                 rutabaga_components.insert(RutabagaComponentType::VirglRenderer, virgl);
978 
979                 push_capset(RUTABAGA_CAPSET_VIRGL);
980                 push_capset(RUTABAGA_CAPSET_VIRGL2);
981                 push_capset(RUTABAGA_CAPSET_VENUS);
982                 push_capset(RUTABAGA_CAPSET_DRM);
983             }
984 
985             #[cfg(feature = "gfxstream")]
986             if self.default_component == RutabagaComponentType::Gfxstream {
987                 let gfxstream = Gfxstream::init(
988                     self.display_width,
989                     self.display_height,
990                     self.gfxstream_flags,
991                     fence_handler.clone(),
992                 )?;
993 
994                 rutabaga_components.insert(RutabagaComponentType::Gfxstream, gfxstream);
995 
996                 push_capset(RUTABAGA_CAPSET_GFXSTREAM);
997             }
998 
999             let cross_domain = CrossDomain::init(self.channels)?;
1000             rutabaga_components.insert(RutabagaComponentType::CrossDomain, cross_domain);
1001             push_capset(RUTABAGA_CAPSET_CROSS_DOMAIN);
1002         }
1003 
1004         Ok(Rutabaga {
1005             resources: Default::default(),
1006             contexts: Default::default(),
1007             components: rutabaga_components,
1008             default_component: self.default_component,
1009             capset_info: rutabaga_capsets,
1010             fence_handler,
1011         })
1012     }
1013 }
1014