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