xref: /aosp_15_r20/external/crosvm/devices/src/virtio/gpu/virtio_gpu.rs (revision bb4ee6a4ae7042d18b07a98463b9c8b875e44b39)
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 use std::cell::RefCell;
6 use std::collections::BTreeMap as Map;
7 use std::collections::BTreeSet as Set;
8 use std::io::IoSliceMut;
9 use std::num::NonZeroU32;
10 use std::rc::Rc;
11 use std::result::Result;
12 use std::sync::atomic::AtomicBool;
13 use std::sync::atomic::Ordering;
14 use std::sync::Arc;
15 
16 use anyhow::Context;
17 use base::error;
18 use base::FromRawDescriptor;
19 use base::IntoRawDescriptor;
20 use base::Protection;
21 use base::SafeDescriptor;
22 use base::VolatileSlice;
23 use gpu_display::*;
24 use hypervisor::MemCacheType;
25 use libc::c_void;
26 use rutabaga_gfx::ResourceCreate3D;
27 use rutabaga_gfx::ResourceCreateBlob;
28 use rutabaga_gfx::Rutabaga;
29 use rutabaga_gfx::RutabagaDescriptor;
30 #[cfg(windows)]
31 use rutabaga_gfx::RutabagaError;
32 use rutabaga_gfx::RutabagaFence;
33 use rutabaga_gfx::RutabagaFromRawDescriptor;
34 use rutabaga_gfx::RutabagaHandle;
35 use rutabaga_gfx::RutabagaIntoRawDescriptor;
36 use rutabaga_gfx::RutabagaIovec;
37 use rutabaga_gfx::Transfer3D;
38 use rutabaga_gfx::RUTABAGA_MAP_ACCESS_MASK;
39 use rutabaga_gfx::RUTABAGA_MAP_ACCESS_READ;
40 use rutabaga_gfx::RUTABAGA_MAP_ACCESS_RW;
41 use rutabaga_gfx::RUTABAGA_MAP_ACCESS_WRITE;
42 use rutabaga_gfx::RUTABAGA_MAP_CACHE_CACHED;
43 use rutabaga_gfx::RUTABAGA_MAP_CACHE_MASK;
44 use rutabaga_gfx::RUTABAGA_MEM_HANDLE_TYPE_DMABUF;
45 use rutabaga_gfx::RUTABAGA_MEM_HANDLE_TYPE_OPAQUE_FD;
46 use serde::Deserialize;
47 use serde::Serialize;
48 use sync::Mutex;
49 use vm_control::gpu::DisplayMode;
50 use vm_control::gpu::DisplayParameters;
51 use vm_control::gpu::GpuControlCommand;
52 use vm_control::gpu::GpuControlResult;
53 use vm_control::gpu::MouseMode;
54 use vm_control::VmMemorySource;
55 use vm_memory::udmabuf::UdmabufDriver;
56 use vm_memory::udmabuf::UdmabufDriverTrait;
57 use vm_memory::GuestAddress;
58 use vm_memory::GuestMemory;
59 
60 use super::protocol::GpuResponse;
61 use super::protocol::GpuResponse::*;
62 use super::protocol::GpuResponsePlaneInfo;
63 use super::protocol::VirtioGpuResult;
64 use super::protocol::VIRTIO_GPU_BLOB_FLAG_CREATE_GUEST_HANDLE;
65 use super::protocol::VIRTIO_GPU_BLOB_MEM_HOST3D;
66 use super::VirtioScanoutBlobData;
67 use crate::virtio::gpu::edid::DisplayInfo;
68 use crate::virtio::gpu::edid::EdidBytes;
69 use crate::virtio::gpu::GpuDisplayParameters;
70 use crate::virtio::gpu::VIRTIO_GPU_MAX_SCANOUTS;
71 use crate::virtio::resource_bridge::BufferInfo;
72 use crate::virtio::resource_bridge::PlaneInfo;
73 use crate::virtio::resource_bridge::ResourceInfo;
74 use crate::virtio::resource_bridge::ResourceResponse;
75 use crate::virtio::SharedMemoryMapper;
76 
to_rutabaga_descriptor(s: SafeDescriptor) -> RutabagaDescriptor77 pub fn to_rutabaga_descriptor(s: SafeDescriptor) -> RutabagaDescriptor {
78     // SAFETY:
79     // Safe because we own the SafeDescriptor at this point.
80     unsafe { RutabagaDescriptor::from_raw_descriptor(s.into_raw_descriptor()) }
81 }
82 
to_safe_descriptor(r: RutabagaDescriptor) -> SafeDescriptor83 fn to_safe_descriptor(r: RutabagaDescriptor) -> SafeDescriptor {
84     // SAFETY:
85     // Safe because we own the SafeDescriptor at this point.
86     unsafe { SafeDescriptor::from_raw_descriptor(r.into_raw_descriptor()) }
87 }
88 
89 struct VirtioGpuResource {
90     resource_id: u32,
91     width: u32,
92     height: u32,
93     size: u64,
94     shmem_offset: Option<u64>,
95     scanout_data: Option<VirtioScanoutBlobData>,
96     display_import: Option<u32>,
97     rutabaga_external_mapping: bool,
98 
99     // Only saved for snapshotting, so that we can re-attach backing iovecs with the correct new
100     // host addresses.
101     backing_iovecs: Option<Vec<(GuestAddress, usize)>>,
102 }
103 
104 #[derive(Serialize, Deserialize)]
105 struct VirtioGpuResourceSnapshot {
106     resource_id: u32,
107     width: u32,
108     height: u32,
109     size: u64,
110 
111     backing_iovecs: Option<Vec<(GuestAddress, usize)>>,
112     shmem_offset: Option<u64>,
113 }
114 
115 impl VirtioGpuResource {
116     /// Creates a new VirtioGpuResource with the given metadata.  Width and height are used by the
117     /// display, while size is useful for hypervisor mapping.
new(resource_id: u32, width: u32, height: u32, size: u64) -> VirtioGpuResource118     pub fn new(resource_id: u32, width: u32, height: u32, size: u64) -> VirtioGpuResource {
119         VirtioGpuResource {
120             resource_id,
121             width,
122             height,
123             size,
124             shmem_offset: None,
125             scanout_data: None,
126             display_import: None,
127             rutabaga_external_mapping: false,
128             backing_iovecs: None,
129         }
130     }
131 
snapshot(&self) -> VirtioGpuResourceSnapshot132     fn snapshot(&self) -> VirtioGpuResourceSnapshot {
133         // Only the 2D backend is fully supported and it doesn't use these fields. 3D is WIP.
134         assert!(self.scanout_data.is_none());
135         assert!(self.display_import.is_none());
136 
137         VirtioGpuResourceSnapshot {
138             resource_id: self.resource_id,
139             width: self.width,
140             height: self.height,
141             size: self.size,
142             backing_iovecs: self.backing_iovecs.clone(),
143             shmem_offset: self.shmem_offset,
144         }
145     }
146 
restore(s: VirtioGpuResourceSnapshot) -> Self147     fn restore(s: VirtioGpuResourceSnapshot) -> Self {
148         let mut resource = VirtioGpuResource::new(s.resource_id, s.width, s.height, s.size);
149         resource.backing_iovecs = s.backing_iovecs;
150         resource
151     }
152 }
153 
154 struct VirtioGpuScanout {
155     width: u32,
156     height: u32,
157     scanout_type: SurfaceType,
158     // If this scanout is a primary scanout, the scanout id.
159     scanout_id: Option<u32>,
160     // If this scanout is a primary scanout, the display properties.
161     display_params: Option<GpuDisplayParameters>,
162     // If this scanout is a cursor scanout, the scanout that this is cursor is overlayed onto.
163     parent_surface_id: Option<u32>,
164 
165     surface_id: Option<u32>,
166     parent_scanout_id: Option<u32>,
167 
168     resource_id: Option<NonZeroU32>,
169     position: Option<(u32, u32)>,
170 }
171 
172 #[derive(Serialize, Deserialize)]
173 struct VirtioGpuScanoutSnapshot {
174     width: u32,
175     height: u32,
176     scanout_type: SurfaceType,
177     scanout_id: Option<u32>,
178     display_params: Option<GpuDisplayParameters>,
179 
180     // The surface IDs aren't guest visible. Instead of storing them and then having to fix up
181     // `gpu_display` internals, we'll allocate new ones on restore. So, we just need to store
182     // whether a surface was allocated and the parent's scanout ID.
183     has_surface: bool,
184     parent_scanout_id: Option<u32>,
185 
186     resource_id: Option<NonZeroU32>,
187     position: Option<(u32, u32)>,
188 }
189 
190 impl VirtioGpuScanout {
new_primary(scanout_id: u32, params: GpuDisplayParameters) -> VirtioGpuScanout191     fn new_primary(scanout_id: u32, params: GpuDisplayParameters) -> VirtioGpuScanout {
192         let (width, height) = params.get_virtual_display_size();
193         VirtioGpuScanout {
194             width,
195             height,
196             scanout_type: SurfaceType::Scanout,
197             scanout_id: Some(scanout_id),
198             display_params: Some(params),
199             parent_surface_id: None,
200             surface_id: None,
201             parent_scanout_id: None,
202             resource_id: None,
203             position: None,
204         }
205     }
206 
new_cursor() -> VirtioGpuScanout207     fn new_cursor() -> VirtioGpuScanout {
208         // Per virtio spec: "The mouse cursor image is a normal resource, except that it must be
209         // 64x64 in size."
210         VirtioGpuScanout {
211             width: 64,
212             height: 64,
213             scanout_type: SurfaceType::Cursor,
214             scanout_id: None,
215             display_params: None,
216             parent_surface_id: None,
217             surface_id: None,
218             parent_scanout_id: None,
219             resource_id: None,
220             position: None,
221         }
222     }
223 
snapshot(&self) -> VirtioGpuScanoutSnapshot224     fn snapshot(&self) -> VirtioGpuScanoutSnapshot {
225         VirtioGpuScanoutSnapshot {
226             width: self.width,
227             height: self.height,
228             has_surface: self.surface_id.is_some(),
229             resource_id: self.resource_id,
230             scanout_type: self.scanout_type,
231             scanout_id: self.scanout_id,
232             display_params: self.display_params.clone(),
233             parent_scanout_id: self.parent_scanout_id,
234             position: self.position,
235         }
236     }
237 
restore( &mut self, snapshot: VirtioGpuScanoutSnapshot, parent_surface_id: Option<u32>, display: &Rc<RefCell<GpuDisplay>>, ) -> VirtioGpuResult238     fn restore(
239         &mut self,
240         snapshot: VirtioGpuScanoutSnapshot,
241         parent_surface_id: Option<u32>,
242         display: &Rc<RefCell<GpuDisplay>>,
243     ) -> VirtioGpuResult {
244         // Scanouts are mainly controlled by the host, we just need to make sure it looks same,
245         // restore the resource_id association, and create a surface in the display.
246 
247         assert_eq!(self.width, snapshot.width);
248         assert_eq!(self.height, snapshot.height);
249         assert_eq!(self.scanout_type, snapshot.scanout_type);
250         assert_eq!(self.scanout_id, snapshot.scanout_id);
251         assert_eq!(self.display_params, snapshot.display_params);
252 
253         self.resource_id = snapshot.resource_id;
254         if snapshot.has_surface {
255             self.create_surface(display, parent_surface_id)?;
256         } else {
257             self.release_surface(display);
258         }
259         if let Some((x, y)) = snapshot.position {
260             self.set_position(display, x, y)?;
261         }
262 
263         Ok(OkNoData)
264     }
265 
create_surface( &mut self, display: &Rc<RefCell<GpuDisplay>>, new_parent_surface_id: Option<u32>, ) -> VirtioGpuResult266     fn create_surface(
267         &mut self,
268         display: &Rc<RefCell<GpuDisplay>>,
269         new_parent_surface_id: Option<u32>,
270     ) -> VirtioGpuResult {
271         let mut need_to_create = false;
272 
273         if self.surface_id.is_none() {
274             need_to_create = true;
275         }
276 
277         if self.parent_surface_id != new_parent_surface_id {
278             self.parent_surface_id = new_parent_surface_id;
279             need_to_create = true;
280         }
281 
282         if !need_to_create {
283             return Ok(OkNoData);
284         }
285 
286         self.release_surface(display);
287 
288         let mut display = display.borrow_mut();
289 
290         let display_params =
291             self.display_params
292                 .clone()
293                 .unwrap_or(DisplayParameters::default_with_mode(DisplayMode::Windowed(
294                     self.width,
295                     self.height,
296                 )));
297         let surface_id = display.create_surface(
298             self.parent_surface_id,
299             self.scanout_id,
300             &display_params,
301             self.scanout_type,
302         )?;
303 
304         self.surface_id = Some(surface_id);
305 
306         Ok(OkNoData)
307     }
308 
release_surface(&mut self, display: &Rc<RefCell<GpuDisplay>>)309     fn release_surface(&mut self, display: &Rc<RefCell<GpuDisplay>>) {
310         if let Some(surface_id) = self.surface_id {
311             display.borrow_mut().release_surface(surface_id);
312         }
313 
314         self.surface_id = None;
315     }
316 
set_mouse_mode( &mut self, display: &Rc<RefCell<GpuDisplay>>, mouse_mode: MouseMode, ) -> VirtioGpuResult317     fn set_mouse_mode(
318         &mut self,
319         display: &Rc<RefCell<GpuDisplay>>,
320         mouse_mode: MouseMode,
321     ) -> VirtioGpuResult {
322         if let Some(surface_id) = self.surface_id {
323             display
324                 .borrow_mut()
325                 .set_mouse_mode(surface_id, mouse_mode)?;
326         }
327         Ok(OkNoData)
328     }
329 
set_position( &mut self, display: &Rc<RefCell<GpuDisplay>>, x: u32, y: u32, ) -> VirtioGpuResult330     fn set_position(
331         &mut self,
332         display: &Rc<RefCell<GpuDisplay>>,
333         x: u32,
334         y: u32,
335     ) -> VirtioGpuResult {
336         if let Some(surface_id) = self.surface_id {
337             display.borrow_mut().set_position(surface_id, x, y)?;
338             self.position = Some((x, y));
339         }
340         Ok(OkNoData)
341     }
342 
commit(&self, display: &Rc<RefCell<GpuDisplay>>) -> VirtioGpuResult343     fn commit(&self, display: &Rc<RefCell<GpuDisplay>>) -> VirtioGpuResult {
344         if let Some(surface_id) = self.surface_id {
345             display.borrow_mut().commit(surface_id)?;
346         }
347         Ok(OkNoData)
348     }
349 
flush( &mut self, display: &Rc<RefCell<GpuDisplay>>, resource: &mut VirtioGpuResource, rutabaga: &mut Rutabaga, ) -> VirtioGpuResult350     fn flush(
351         &mut self,
352         display: &Rc<RefCell<GpuDisplay>>,
353         resource: &mut VirtioGpuResource,
354         rutabaga: &mut Rutabaga,
355     ) -> VirtioGpuResult {
356         let surface_id = match self.surface_id {
357             Some(id) => id,
358             _ => return Ok(OkNoData),
359         };
360 
361         if let Some(import_id) =
362             VirtioGpuScanout::import_resource_to_display(display, surface_id, resource, rutabaga)
363         {
364             display
365                 .borrow_mut()
366                 .flip_to(surface_id, import_id, None, None, None)
367                 .map_err(|e| {
368                     error!("flip_to failed: {:#}", e);
369                     ErrUnspec
370                 })?;
371             return Ok(OkNoData);
372         }
373 
374         // Import failed, fall back to a copy.
375         let mut display = display.borrow_mut();
376 
377         // Prevent overwriting a buffer that is currently being used by the compositor.
378         if display.next_buffer_in_use(surface_id) {
379             return Ok(OkNoData);
380         }
381 
382         let fb = display
383             .framebuffer_region(surface_id, 0, 0, self.width, self.height)
384             .ok_or(ErrUnspec)?;
385 
386         let mut transfer = Transfer3D::new_2d(0, 0, self.width, self.height, 0);
387         transfer.stride = fb.stride();
388         let fb_slice = fb.as_volatile_slice();
389         let buf = IoSliceMut::new(
390             // SAFETY: trivially safe
391             unsafe { std::slice::from_raw_parts_mut(fb_slice.as_mut_ptr(), fb_slice.size()) },
392         );
393         rutabaga.transfer_read(0, resource.resource_id, transfer, Some(buf))?;
394 
395         display.flip(surface_id);
396         Ok(OkNoData)
397     }
398 
import_resource_to_display( display: &Rc<RefCell<GpuDisplay>>, surface_id: u32, resource: &mut VirtioGpuResource, rutabaga: &mut Rutabaga, ) -> Option<u32>399     fn import_resource_to_display(
400         display: &Rc<RefCell<GpuDisplay>>,
401         surface_id: u32,
402         resource: &mut VirtioGpuResource,
403         rutabaga: &mut Rutabaga,
404     ) -> Option<u32> {
405         if let Some(import_id) = resource.display_import {
406             return Some(import_id);
407         }
408 
409         let dmabuf = to_safe_descriptor(rutabaga.export_blob(resource.resource_id).ok()?.os_handle);
410         let query = rutabaga.query(resource.resource_id).ok()?;
411 
412         let (width, height, format, stride, offset) = match resource.scanout_data {
413             Some(data) => (
414                 data.width,
415                 data.height,
416                 data.drm_format.into(),
417                 data.strides[0],
418                 data.offsets[0],
419             ),
420             None => (
421                 resource.width,
422                 resource.height,
423                 query.drm_fourcc,
424                 query.strides[0],
425                 query.offsets[0],
426             ),
427         };
428 
429         let import_id = display
430             .borrow_mut()
431             .import_resource(
432                 surface_id,
433                 DisplayExternalResourceImport::Dmabuf {
434                     descriptor: &dmabuf,
435                     offset,
436                     stride,
437                     modifiers: query.modifier,
438                     width,
439                     height,
440                     fourcc: format,
441                 },
442             )
443             .ok()?;
444         resource.display_import = Some(import_id);
445         Some(import_id)
446     }
447 }
448 
449 /// Handles functionality related to displays, input events and hypervisor memory management.
450 pub struct VirtioGpu {
451     display: Rc<RefCell<GpuDisplay>>,
452     scanouts: Map<u32, VirtioGpuScanout>,
453     scanouts_updated: Arc<AtomicBool>,
454     cursor_scanout: VirtioGpuScanout,
455     mapper: Arc<Mutex<Option<Box<dyn SharedMemoryMapper>>>>,
456     rutabaga: Rutabaga,
457     resources: Map<u32, VirtioGpuResource>,
458     external_blob: bool,
459     fixed_blob_mapping: bool,
460     udmabuf_driver: Option<UdmabufDriver>,
461     deferred_snapshot_load: Option<VirtioGpuSnapshot>,
462 }
463 
464 // Only the 2D mode is supported. Notes on `VirtioGpu` fields:
465 //
466 //   * display: re-initialized from scratch using the scanout snapshots
467 //   * scanouts: snapshot'd
468 //   * scanouts_updated: snapshot'd
469 //   * cursor_scanout: snapshot'd
470 //   * mapper: not needed for 2d mode
471 //   * rutabaga: re-initialized from scatch using the resource snapshots
472 //   * resources: snapshot'd
473 //   * external_blob: not needed for 2d mode
474 //   * udmabuf_driver: not needed for 2d mode
475 #[derive(Serialize, Deserialize)]
476 pub struct VirtioGpuSnapshot {
477     scanouts: Map<u32, VirtioGpuScanoutSnapshot>,
478     scanouts_updated: bool,
479     cursor_scanout: VirtioGpuScanoutSnapshot,
480     rutabaga: Vec<u8>,
481     resources: Map<u32, VirtioGpuResourceSnapshot>,
482 }
483 
484 #[derive(Serialize, Deserialize)]
485 struct RutabagaResourceSnapshotSerializable {
486     resource_id: u32,
487 
488     width: u32,
489     height: u32,
490     host_mem_size: usize,
491 
492     backing_iovecs: Option<Vec<(GuestAddress, usize)>>,
493     component_mask: u8,
494     size: u64,
495 }
496 
sglist_to_rutabaga_iovecs( vecs: &[(GuestAddress, usize)], mem: &GuestMemory, ) -> Result<Vec<RutabagaIovec>, ()>497 fn sglist_to_rutabaga_iovecs(
498     vecs: &[(GuestAddress, usize)],
499     mem: &GuestMemory,
500 ) -> Result<Vec<RutabagaIovec>, ()> {
501     if vecs
502         .iter()
503         .any(|&(addr, len)| mem.get_slice_at_addr(addr, len).is_err())
504     {
505         return Err(());
506     }
507 
508     let mut rutabaga_iovecs: Vec<RutabagaIovec> = Vec::new();
509     for &(addr, len) in vecs {
510         let slice = mem.get_slice_at_addr(addr, len).unwrap();
511         rutabaga_iovecs.push(RutabagaIovec {
512             base: slice.as_mut_ptr() as *mut c_void,
513             len,
514         });
515     }
516     Ok(rutabaga_iovecs)
517 }
518 
519 pub enum ProcessDisplayResult {
520     Success,
521     CloseRequested,
522     Error(GpuDisplayError),
523 }
524 
525 impl VirtioGpu {
526     /// Creates a new instance of the VirtioGpu state tracker.
new( display: GpuDisplay, display_params: Vec<GpuDisplayParameters>, display_event: Arc<AtomicBool>, rutabaga: Rutabaga, mapper: Arc<Mutex<Option<Box<dyn SharedMemoryMapper>>>>, external_blob: bool, fixed_blob_mapping: bool, udmabuf: bool, ) -> Option<VirtioGpu>527     pub fn new(
528         display: GpuDisplay,
529         display_params: Vec<GpuDisplayParameters>,
530         display_event: Arc<AtomicBool>,
531         rutabaga: Rutabaga,
532         mapper: Arc<Mutex<Option<Box<dyn SharedMemoryMapper>>>>,
533         external_blob: bool,
534         fixed_blob_mapping: bool,
535         udmabuf: bool,
536     ) -> Option<VirtioGpu> {
537         let mut udmabuf_driver = None;
538         if udmabuf {
539             udmabuf_driver = Some(
540                 UdmabufDriver::new()
541                     .map_err(|e| error!("failed to initialize udmabuf: {}", e))
542                     .ok()?,
543             );
544         }
545 
546         let scanouts = display_params
547             .iter()
548             .enumerate()
549             .map(|(display_index, display_param)| {
550                 (
551                     display_index as u32,
552                     VirtioGpuScanout::new_primary(display_index as u32, display_param.clone()),
553                 )
554             })
555             .collect::<Map<_, _>>();
556         let cursor_scanout = VirtioGpuScanout::new_cursor();
557 
558         Some(VirtioGpu {
559             display: Rc::new(RefCell::new(display)),
560             scanouts,
561             scanouts_updated: display_event,
562             cursor_scanout,
563             mapper,
564             rutabaga,
565             resources: Default::default(),
566             external_blob,
567             fixed_blob_mapping,
568             udmabuf_driver,
569             deferred_snapshot_load: None,
570         })
571     }
572 
573     /// Imports the event device
import_event_device(&mut self, event_device: EventDevice) -> VirtioGpuResult574     pub fn import_event_device(&mut self, event_device: EventDevice) -> VirtioGpuResult {
575         let mut display = self.display.borrow_mut();
576         let _event_device_id = display.import_event_device(event_device)?;
577         Ok(OkNoData)
578     }
579 
580     /// Gets a reference to the display passed into `new`.
display(&mut self) -> &Rc<RefCell<GpuDisplay>>581     pub fn display(&mut self) -> &Rc<RefCell<GpuDisplay>> {
582         &self.display
583     }
584 
585     /// Gets the list of supported display resolutions as a slice of `(width, height, enabled)`
586     /// tuples.
display_info(&self) -> Vec<(u32, u32, bool)>587     pub fn display_info(&self) -> Vec<(u32, u32, bool)> {
588         (0..VIRTIO_GPU_MAX_SCANOUTS)
589             .map(|scanout_id| scanout_id as u32)
590             .map(|scanout_id| {
591                 self.scanouts
592                     .get(&scanout_id)
593                     .map_or((0, 0, false), |scanout| {
594                         (scanout.width, scanout.height, true)
595                     })
596             })
597             .collect::<Vec<_>>()
598     }
599 
600     // Connects new displays to the device.
add_displays(&mut self, displays: Vec<DisplayParameters>) -> GpuControlResult601     fn add_displays(&mut self, displays: Vec<DisplayParameters>) -> GpuControlResult {
602         let requested_num_scanouts = self.scanouts.len() + displays.len();
603         if requested_num_scanouts > VIRTIO_GPU_MAX_SCANOUTS {
604             return GpuControlResult::TooManyDisplays {
605                 allowed: VIRTIO_GPU_MAX_SCANOUTS,
606                 requested: requested_num_scanouts,
607             };
608         }
609 
610         let mut available_scanout_ids = (0..VIRTIO_GPU_MAX_SCANOUTS)
611             .map(|s| s as u32)
612             .collect::<Set<u32>>();
613 
614         self.scanouts.keys().for_each(|scanout_id| {
615             available_scanout_ids.remove(scanout_id);
616         });
617 
618         for display_params in displays.into_iter() {
619             let new_scanout_id = *available_scanout_ids.iter().next().unwrap();
620             available_scanout_ids.remove(&new_scanout_id);
621 
622             self.scanouts.insert(
623                 new_scanout_id,
624                 VirtioGpuScanout::new_primary(new_scanout_id, display_params),
625             );
626         }
627 
628         self.scanouts_updated.store(true, Ordering::Relaxed);
629 
630         GpuControlResult::DisplaysUpdated
631     }
632 
633     /// Returns the list of displays currently connected to the device.
list_displays(&self) -> GpuControlResult634     fn list_displays(&self) -> GpuControlResult {
635         GpuControlResult::DisplayList {
636             displays: self
637                 .scanouts
638                 .iter()
639                 .filter_map(|(scanout_id, scanout)| {
640                     scanout
641                         .display_params
642                         .as_ref()
643                         .cloned()
644                         .map(|display_params| (*scanout_id, display_params))
645                 })
646                 .collect(),
647         }
648     }
649 
650     /// Removes the specified displays from the device.
remove_displays(&mut self, display_ids: Vec<u32>) -> GpuControlResult651     fn remove_displays(&mut self, display_ids: Vec<u32>) -> GpuControlResult {
652         for display_id in display_ids {
653             if let Some(mut scanout) = self.scanouts.remove(&display_id) {
654                 scanout.release_surface(&self.display);
655             } else {
656                 return GpuControlResult::NoSuchDisplay { display_id };
657             }
658         }
659 
660         self.scanouts_updated.store(true, Ordering::Relaxed);
661         GpuControlResult::DisplaysUpdated
662     }
663 
set_display_mouse_mode( &mut self, display_id: u32, mouse_mode: MouseMode, ) -> GpuControlResult664     fn set_display_mouse_mode(
665         &mut self,
666         display_id: u32,
667         mouse_mode: MouseMode,
668     ) -> GpuControlResult {
669         match self.scanouts.get_mut(&display_id) {
670             Some(scanout) => match scanout.set_mouse_mode(&self.display, mouse_mode) {
671                 Ok(_) => GpuControlResult::DisplayMouseModeSet,
672                 Err(e) => GpuControlResult::ErrString(e.to_string()),
673             },
674             None => GpuControlResult::NoSuchDisplay { display_id },
675         }
676     }
677 
678     /// Performs the given command to interact with or modify the device.
process_gpu_control_command(&mut self, cmd: GpuControlCommand) -> GpuControlResult679     pub fn process_gpu_control_command(&mut self, cmd: GpuControlCommand) -> GpuControlResult {
680         match cmd {
681             GpuControlCommand::AddDisplays { displays } => self.add_displays(displays),
682             GpuControlCommand::ListDisplays => self.list_displays(),
683             GpuControlCommand::RemoveDisplays { display_ids } => self.remove_displays(display_ids),
684             GpuControlCommand::SetDisplayMouseMode {
685                 display_id,
686                 mouse_mode,
687             } => self.set_display_mouse_mode(display_id, mouse_mode),
688         }
689     }
690 
691     /// Processes the internal `display` events and returns `true` if any display was closed.
process_display(&mut self) -> ProcessDisplayResult692     pub fn process_display(&mut self) -> ProcessDisplayResult {
693         let mut display = self.display.borrow_mut();
694         let result = display.dispatch_events();
695         match result {
696             Ok(_) => (),
697             Err(e) => {
698                 error!("failed to dispatch events: {}", e);
699                 return ProcessDisplayResult::Error(e);
700             }
701         }
702 
703         for scanout in self.scanouts.values() {
704             let close_requested = scanout
705                 .surface_id
706                 .map(|surface_id| display.close_requested(surface_id))
707                 .unwrap_or(false);
708 
709             if close_requested {
710                 return ProcessDisplayResult::CloseRequested;
711             }
712         }
713 
714         ProcessDisplayResult::Success
715     }
716 
717     /// Sets the given resource id as the source of scanout to the display.
set_scanout( &mut self, scanout_id: u32, resource_id: u32, scanout_data: Option<VirtioScanoutBlobData>, ) -> VirtioGpuResult718     pub fn set_scanout(
719         &mut self,
720         scanout_id: u32,
721         resource_id: u32,
722         scanout_data: Option<VirtioScanoutBlobData>,
723     ) -> VirtioGpuResult {
724         self.update_scanout_resource(SurfaceType::Scanout, scanout_id, scanout_data, resource_id)
725     }
726 
727     /// If the resource is the scanout resource, flush it to the display.
flush_resource(&mut self, resource_id: u32) -> VirtioGpuResult728     pub fn flush_resource(&mut self, resource_id: u32) -> VirtioGpuResult {
729         if resource_id == 0 {
730             return Ok(OkNoData);
731         }
732 
733         #[cfg(windows)]
734         match self.rutabaga.resource_flush(resource_id) {
735             Ok(_) => return Ok(OkNoData),
736             Err(RutabagaError::Unsupported) => {}
737             Err(e) => return Err(ErrRutabaga(e)),
738         }
739 
740         let resource = self
741             .resources
742             .get_mut(&resource_id)
743             .ok_or(ErrInvalidResourceId)?;
744 
745         // `resource_id` has already been verified to be non-zero
746         let resource_id = match NonZeroU32::new(resource_id) {
747             Some(id) => Some(id),
748             None => return Ok(OkNoData),
749         };
750 
751         for scanout in self.scanouts.values_mut() {
752             if scanout.resource_id == resource_id {
753                 scanout.flush(&self.display, resource, &mut self.rutabaga)?;
754             }
755         }
756         if self.cursor_scanout.resource_id == resource_id {
757             self.cursor_scanout
758                 .flush(&self.display, resource, &mut self.rutabaga)?;
759         }
760 
761         Ok(OkNoData)
762     }
763 
764     /// Updates the cursor's memory to the given resource_id, and sets its position to the given
765     /// coordinates.
update_cursor( &mut self, resource_id: u32, scanout_id: u32, x: u32, y: u32, ) -> VirtioGpuResult766     pub fn update_cursor(
767         &mut self,
768         resource_id: u32,
769         scanout_id: u32,
770         x: u32,
771         y: u32,
772     ) -> VirtioGpuResult {
773         self.update_scanout_resource(SurfaceType::Cursor, scanout_id, None, resource_id)?;
774 
775         self.cursor_scanout.set_position(&self.display, x, y)?;
776 
777         self.flush_resource(resource_id)
778     }
779 
780     /// Moves the cursor's position to the given coordinates.
move_cursor(&mut self, _scanout_id: u32, x: u32, y: u32) -> VirtioGpuResult781     pub fn move_cursor(&mut self, _scanout_id: u32, x: u32, y: u32) -> VirtioGpuResult {
782         self.cursor_scanout.set_position(&self.display, x, y)?;
783         self.cursor_scanout.commit(&self.display)?;
784         Ok(OkNoData)
785     }
786 
787     /// Returns a uuid for the resource.
resource_assign_uuid(&self, resource_id: u32) -> VirtioGpuResult788     pub fn resource_assign_uuid(&self, resource_id: u32) -> VirtioGpuResult {
789         if !self.resources.contains_key(&resource_id) {
790             return Err(ErrInvalidResourceId);
791         }
792 
793         // TODO(stevensd): use real uuids once the virtio wayland protocol is updated to
794         // handle more than 32 bits. For now, the virtwl driver knows that the uuid is
795         // actually just the resource id.
796         let mut uuid: [u8; 16] = [0; 16];
797         for (idx, byte) in resource_id.to_be_bytes().iter().enumerate() {
798             uuid[12 + idx] = *byte;
799         }
800         Ok(OkResourceUuid { uuid })
801     }
802 
803     /// If supported, export the resource with the given `resource_id` to a file.
export_resource(&mut self, resource_id: u32) -> ResourceResponse804     pub fn export_resource(&mut self, resource_id: u32) -> ResourceResponse {
805         let handle = match self.rutabaga.export_blob(resource_id) {
806             Ok(handle) => to_safe_descriptor(handle.os_handle),
807             Err(_) => return ResourceResponse::Invalid,
808         };
809 
810         let q = match self.rutabaga.query(resource_id) {
811             Ok(query) => query,
812             Err(_) => return ResourceResponse::Invalid,
813         };
814 
815         ResourceResponse::Resource(ResourceInfo::Buffer(BufferInfo {
816             handle,
817             planes: [
818                 PlaneInfo {
819                     offset: q.offsets[0],
820                     stride: q.strides[0],
821                 },
822                 PlaneInfo {
823                     offset: q.offsets[1],
824                     stride: q.strides[1],
825                 },
826                 PlaneInfo {
827                     offset: q.offsets[2],
828                     stride: q.strides[2],
829                 },
830                 PlaneInfo {
831                     offset: q.offsets[3],
832                     stride: q.strides[3],
833                 },
834             ],
835             modifier: q.modifier,
836             guest_cpu_mappable: q.guest_cpu_mappable,
837         }))
838     }
839 
840     /// If supported, export the fence with the given `fence_id` to a file.
export_fence(&mut self, fence_id: u64) -> ResourceResponse841     pub fn export_fence(&mut self, fence_id: u64) -> ResourceResponse {
842         match self.rutabaga.export_fence(fence_id) {
843             Ok(handle) => ResourceResponse::Resource(ResourceInfo::Fence {
844                 handle: to_safe_descriptor(handle.os_handle),
845             }),
846             Err(_) => ResourceResponse::Invalid,
847         }
848     }
849 
850     /// Gets rutabaga's capset information associated with `index`.
get_capset_info(&self, index: u32) -> VirtioGpuResult851     pub fn get_capset_info(&self, index: u32) -> VirtioGpuResult {
852         if let Ok((capset_id, version, size)) = self.rutabaga.get_capset_info(index) {
853             Ok(OkCapsetInfo {
854                 capset_id,
855                 version,
856                 size,
857             })
858         } else {
859             // Any capset_id > 63 is invalid according to the virtio-gpu spec, so we can
860             // intentionally poison the capset without stalling the guest kernel driver.
861             base::warn!(
862                 "virtio-gpu get_capset_info(index={}) failed. intentionally poisoning response",
863                 index
864             );
865             Ok(OkCapsetInfo {
866                 capset_id: u32::MAX,
867                 version: 0,
868                 size: 0,
869             })
870         }
871     }
872 
873     /// Gets a capset from rutabaga.
get_capset(&self, capset_id: u32, version: u32) -> VirtioGpuResult874     pub fn get_capset(&self, capset_id: u32, version: u32) -> VirtioGpuResult {
875         let capset = self.rutabaga.get_capset(capset_id, version)?;
876         Ok(OkCapset(capset))
877     }
878 
879     /// Forces rutabaga to use it's default context.
force_ctx_0(&self)880     pub fn force_ctx_0(&self) {
881         self.rutabaga.force_ctx_0()
882     }
883 
884     /// Creates a fence with the RutabagaFence that can be used to determine when the previous
885     /// command completed.
create_fence(&mut self, rutabaga_fence: RutabagaFence) -> VirtioGpuResult886     pub fn create_fence(&mut self, rutabaga_fence: RutabagaFence) -> VirtioGpuResult {
887         self.rutabaga.create_fence(rutabaga_fence)?;
888         Ok(OkNoData)
889     }
890 
891     /// Polls the Rutabaga backend.
event_poll(&self)892     pub fn event_poll(&self) {
893         self.rutabaga.event_poll();
894     }
895 
896     /// Gets a pollable eventfd that signals the device to wakeup and poll the
897     /// Rutabaga backend.
poll_descriptor(&self) -> Option<SafeDescriptor>898     pub fn poll_descriptor(&self) -> Option<SafeDescriptor> {
899         self.rutabaga.poll_descriptor().map(to_safe_descriptor)
900     }
901 
902     /// Creates a 3D resource with the given properties and resource_id.
resource_create_3d( &mut self, resource_id: u32, resource_create_3d: ResourceCreate3D, ) -> VirtioGpuResult903     pub fn resource_create_3d(
904         &mut self,
905         resource_id: u32,
906         resource_create_3d: ResourceCreate3D,
907     ) -> VirtioGpuResult {
908         self.rutabaga
909             .resource_create_3d(resource_id, resource_create_3d)?;
910 
911         let resource = VirtioGpuResource::new(
912             resource_id,
913             resource_create_3d.width,
914             resource_create_3d.height,
915             0,
916         );
917 
918         // Rely on rutabaga to check for duplicate resource ids.
919         self.resources.insert(resource_id, resource);
920         Ok(self.result_from_query(resource_id))
921     }
922 
923     /// Attaches backing memory to the given resource, represented by a `Vec` of `(address, size)`
924     /// tuples in the guest's physical address space. Converts to RutabagaIovec from the memory
925     /// mapping.
attach_backing( &mut self, resource_id: u32, mem: &GuestMemory, vecs: Vec<(GuestAddress, usize)>, ) -> VirtioGpuResult926     pub fn attach_backing(
927         &mut self,
928         resource_id: u32,
929         mem: &GuestMemory,
930         vecs: Vec<(GuestAddress, usize)>,
931     ) -> VirtioGpuResult {
932         let resource = self
933             .resources
934             .get_mut(&resource_id)
935             .ok_or(ErrInvalidResourceId)?;
936 
937         let rutabaga_iovecs = sglist_to_rutabaga_iovecs(&vecs[..], mem).map_err(|_| ErrUnspec)?;
938         self.rutabaga.attach_backing(resource_id, rutabaga_iovecs)?;
939         resource.backing_iovecs = Some(vecs);
940         Ok(OkNoData)
941     }
942 
943     /// Detaches any previously attached iovecs from the resource.
detach_backing(&mut self, resource_id: u32) -> VirtioGpuResult944     pub fn detach_backing(&mut self, resource_id: u32) -> VirtioGpuResult {
945         let resource = self
946             .resources
947             .get_mut(&resource_id)
948             .ok_or(ErrInvalidResourceId)?;
949 
950         self.rutabaga.detach_backing(resource_id)?;
951         resource.backing_iovecs = None;
952         Ok(OkNoData)
953     }
954 
955     /// Releases guest kernel reference on the resource.
unref_resource(&mut self, resource_id: u32) -> VirtioGpuResult956     pub fn unref_resource(&mut self, resource_id: u32) -> VirtioGpuResult {
957         let resource = self
958             .resources
959             .remove(&resource_id)
960             .ok_or(ErrInvalidResourceId)?;
961 
962         if resource.rutabaga_external_mapping {
963             self.rutabaga.unmap(resource_id)?;
964         }
965 
966         self.rutabaga.unref_resource(resource_id)?;
967         Ok(OkNoData)
968     }
969 
970     /// Copies data to host resource from the attached iovecs. Can also be used to flush caches.
transfer_write( &mut self, ctx_id: u32, resource_id: u32, transfer: Transfer3D, ) -> VirtioGpuResult971     pub fn transfer_write(
972         &mut self,
973         ctx_id: u32,
974         resource_id: u32,
975         transfer: Transfer3D,
976     ) -> VirtioGpuResult {
977         self.rutabaga
978             .transfer_write(ctx_id, resource_id, transfer)?;
979         Ok(OkNoData)
980     }
981 
982     /// Copies data from the host resource to:
983     ///    1) To the optional volatile slice
984     ///    2) To the host resource's attached iovecs
985     ///
986     /// Can also be used to invalidate caches.
transfer_read( &mut self, ctx_id: u32, resource_id: u32, transfer: Transfer3D, buf: Option<VolatileSlice>, ) -> VirtioGpuResult987     pub fn transfer_read(
988         &mut self,
989         ctx_id: u32,
990         resource_id: u32,
991         transfer: Transfer3D,
992         buf: Option<VolatileSlice>,
993     ) -> VirtioGpuResult {
994         let buf = buf.map(|vs| {
995             IoSliceMut::new(
996                 // SAFETY: trivially safe
997                 unsafe { std::slice::from_raw_parts_mut(vs.as_mut_ptr(), vs.size()) },
998             )
999         });
1000         self.rutabaga
1001             .transfer_read(ctx_id, resource_id, transfer, buf)?;
1002         Ok(OkNoData)
1003     }
1004 
1005     /// Creates a blob resource using rutabaga.
resource_create_blob( &mut self, ctx_id: u32, resource_id: u32, resource_create_blob: ResourceCreateBlob, vecs: Vec<(GuestAddress, usize)>, mem: &GuestMemory, ) -> VirtioGpuResult1006     pub fn resource_create_blob(
1007         &mut self,
1008         ctx_id: u32,
1009         resource_id: u32,
1010         resource_create_blob: ResourceCreateBlob,
1011         vecs: Vec<(GuestAddress, usize)>,
1012         mem: &GuestMemory,
1013     ) -> VirtioGpuResult {
1014         let mut descriptor = None;
1015         let mut rutabaga_iovecs = None;
1016 
1017         if resource_create_blob.blob_flags & VIRTIO_GPU_BLOB_FLAG_CREATE_GUEST_HANDLE != 0 {
1018             descriptor = match self.udmabuf_driver {
1019                 Some(ref driver) => Some(driver.create_udmabuf(mem, &vecs[..])?),
1020                 None => return Err(ErrUnspec),
1021             }
1022         } else if resource_create_blob.blob_mem != VIRTIO_GPU_BLOB_MEM_HOST3D {
1023             rutabaga_iovecs =
1024                 Some(sglist_to_rutabaga_iovecs(&vecs[..], mem).map_err(|_| ErrUnspec)?);
1025         }
1026 
1027         self.rutabaga.resource_create_blob(
1028             ctx_id,
1029             resource_id,
1030             resource_create_blob,
1031             rutabaga_iovecs,
1032             descriptor.map(|descriptor| RutabagaHandle {
1033                 os_handle: to_rutabaga_descriptor(descriptor),
1034                 handle_type: RUTABAGA_MEM_HANDLE_TYPE_DMABUF,
1035             }),
1036         )?;
1037 
1038         let resource = VirtioGpuResource::new(resource_id, 0, 0, resource_create_blob.size);
1039 
1040         // Rely on rutabaga to check for duplicate resource ids.
1041         self.resources.insert(resource_id, resource);
1042         Ok(self.result_from_query(resource_id))
1043     }
1044 
1045     /// Uses the hypervisor to map the rutabaga blob resource.
1046     ///
1047     /// When sandboxing is disabled, external_blob is unset and opaque fds are mapped by
1048     /// rutabaga as ExternalMapping.
1049     /// When sandboxing is enabled, external_blob is set and opaque fds must be mapped in the
1050     /// hypervisor process by Vulkano using metadata provided by Rutabaga::vulkan_info().
resource_map_blob(&mut self, resource_id: u32, offset: u64) -> VirtioGpuResult1051     pub fn resource_map_blob(&mut self, resource_id: u32, offset: u64) -> VirtioGpuResult {
1052         let resource = self
1053             .resources
1054             .get_mut(&resource_id)
1055             .ok_or(ErrInvalidResourceId)?;
1056 
1057         let map_info = self.rutabaga.map_info(resource_id).map_err(|_| ErrUnspec)?;
1058 
1059         let mut source: Option<VmMemorySource> = None;
1060         if let Ok(export) = self.rutabaga.export_blob(resource_id) {
1061             if let Ok(vulkan_info) = self.rutabaga.vulkan_info(resource_id) {
1062                 source = Some(VmMemorySource::Vulkan {
1063                     descriptor: to_safe_descriptor(export.os_handle),
1064                     handle_type: export.handle_type,
1065                     memory_idx: vulkan_info.memory_idx,
1066                     device_uuid: vulkan_info.device_id.device_uuid,
1067                     driver_uuid: vulkan_info.device_id.driver_uuid,
1068                     size: resource.size,
1069                 });
1070             } else if export.handle_type != RUTABAGA_MEM_HANDLE_TYPE_OPAQUE_FD {
1071                 source = Some(VmMemorySource::Descriptor {
1072                     descriptor: to_safe_descriptor(export.os_handle),
1073                     offset: 0,
1074                     size: resource.size,
1075                 });
1076             }
1077         }
1078 
1079         // fallback to ExternalMapping via rutabaga if sandboxing (hence external_blob) and fixed
1080         // mapping are both disabled as neither is currently compatible.
1081         if source.is_none() {
1082             if self.external_blob || self.fixed_blob_mapping {
1083                 return Err(ErrUnspec);
1084             }
1085 
1086             let mapping = self.rutabaga.map(resource_id)?;
1087             // resources mapped via rutabaga must also be marked for unmap via rutabaga.
1088             resource.rutabaga_external_mapping = true;
1089             source = Some(VmMemorySource::ExternalMapping {
1090                 ptr: mapping.ptr,
1091                 size: mapping.size,
1092             });
1093         };
1094 
1095         let prot = match map_info & RUTABAGA_MAP_ACCESS_MASK {
1096             RUTABAGA_MAP_ACCESS_READ => Protection::read(),
1097             RUTABAGA_MAP_ACCESS_WRITE => Protection::write(),
1098             RUTABAGA_MAP_ACCESS_RW => Protection::read_write(),
1099             _ => return Err(ErrUnspec),
1100         };
1101 
1102         let cache = if cfg!(feature = "noncoherent-dma")
1103             && map_info & RUTABAGA_MAP_CACHE_MASK != RUTABAGA_MAP_CACHE_CACHED
1104         {
1105             MemCacheType::CacheNonCoherent
1106         } else {
1107             MemCacheType::CacheCoherent
1108         };
1109 
1110         self.mapper
1111             .lock()
1112             .as_mut()
1113             .expect("No backend request connection found")
1114             .add_mapping(source.unwrap(), offset, prot, cache)
1115             .map_err(|_| ErrUnspec)?;
1116 
1117         resource.shmem_offset = Some(offset);
1118         // Access flags not a part of the virtio-gpu spec.
1119         Ok(OkMapInfo {
1120             map_info: map_info & RUTABAGA_MAP_CACHE_MASK,
1121         })
1122     }
1123 
1124     /// Uses the hypervisor to unmap the blob resource.
resource_unmap_blob(&mut self, resource_id: u32) -> VirtioGpuResult1125     pub fn resource_unmap_blob(&mut self, resource_id: u32) -> VirtioGpuResult {
1126         let resource = self
1127             .resources
1128             .get_mut(&resource_id)
1129             .ok_or(ErrInvalidResourceId)?;
1130 
1131         let shmem_offset = resource.shmem_offset.ok_or(ErrUnspec)?;
1132         self.mapper
1133             .lock()
1134             .as_mut()
1135             .expect("No backend request connection found")
1136             .remove_mapping(shmem_offset)
1137             .map_err(|_| ErrUnspec)?;
1138         resource.shmem_offset = None;
1139 
1140         if resource.rutabaga_external_mapping {
1141             self.rutabaga.unmap(resource_id)?;
1142             resource.rutabaga_external_mapping = false;
1143         }
1144 
1145         Ok(OkNoData)
1146     }
1147 
1148     /// Gets the EDID for the specified scanout ID. If that scanout is not enabled, it would return
1149     /// the EDID of a default display.
get_edid(&self, scanout_id: u32) -> VirtioGpuResult1150     pub fn get_edid(&self, scanout_id: u32) -> VirtioGpuResult {
1151         let display_info = match self.scanouts.get(&scanout_id) {
1152             Some(scanout) => {
1153                 // Primary scanouts should always have display params.
1154                 let params = scanout.display_params.as_ref().unwrap();
1155                 DisplayInfo::new(params)
1156             }
1157             None => DisplayInfo::new(&Default::default()),
1158         };
1159         EdidBytes::new(&display_info)
1160     }
1161 
1162     /// Creates a rutabaga context.
create_context( &mut self, ctx_id: u32, context_init: u32, context_name: Option<&str>, ) -> VirtioGpuResult1163     pub fn create_context(
1164         &mut self,
1165         ctx_id: u32,
1166         context_init: u32,
1167         context_name: Option<&str>,
1168     ) -> VirtioGpuResult {
1169         self.rutabaga
1170             .create_context(ctx_id, context_init, context_name)?;
1171         Ok(OkNoData)
1172     }
1173 
1174     /// Destroys a rutabaga context.
destroy_context(&mut self, ctx_id: u32) -> VirtioGpuResult1175     pub fn destroy_context(&mut self, ctx_id: u32) -> VirtioGpuResult {
1176         self.rutabaga.destroy_context(ctx_id)?;
1177         Ok(OkNoData)
1178     }
1179 
1180     /// Attaches a resource to a rutabaga context.
context_attach_resource(&mut self, ctx_id: u32, resource_id: u32) -> VirtioGpuResult1181     pub fn context_attach_resource(&mut self, ctx_id: u32, resource_id: u32) -> VirtioGpuResult {
1182         self.rutabaga.context_attach_resource(ctx_id, resource_id)?;
1183         Ok(OkNoData)
1184     }
1185 
1186     /// Detaches a resource from a rutabaga context.
context_detach_resource(&mut self, ctx_id: u32, resource_id: u32) -> VirtioGpuResult1187     pub fn context_detach_resource(&mut self, ctx_id: u32, resource_id: u32) -> VirtioGpuResult {
1188         self.rutabaga.context_detach_resource(ctx_id, resource_id)?;
1189         Ok(OkNoData)
1190     }
1191 
1192     /// Submits a command buffer to a rutabaga context.
submit_command( &mut self, ctx_id: u32, commands: &mut [u8], fence_ids: &[u64], ) -> VirtioGpuResult1193     pub fn submit_command(
1194         &mut self,
1195         ctx_id: u32,
1196         commands: &mut [u8],
1197         fence_ids: &[u64],
1198     ) -> VirtioGpuResult {
1199         self.rutabaga.submit_command(ctx_id, commands, fence_ids)?;
1200         Ok(OkNoData)
1201     }
1202 
1203     // Non-public function -- no doc comment needed!
result_from_query(&mut self, resource_id: u32) -> GpuResponse1204     fn result_from_query(&mut self, resource_id: u32) -> GpuResponse {
1205         match self.rutabaga.query(resource_id) {
1206             Ok(query) => {
1207                 let mut plane_info = Vec::with_capacity(4);
1208                 for plane_index in 0..4 {
1209                     plane_info.push(GpuResponsePlaneInfo {
1210                         stride: query.strides[plane_index],
1211                         offset: query.offsets[plane_index],
1212                     });
1213                 }
1214                 let format_modifier = query.modifier;
1215                 OkResourcePlaneInfo {
1216                     format_modifier,
1217                     plane_info,
1218                 }
1219             }
1220             Err(_) => OkNoData,
1221         }
1222     }
1223 
update_scanout_resource( &mut self, scanout_type: SurfaceType, scanout_id: u32, scanout_data: Option<VirtioScanoutBlobData>, resource_id: u32, ) -> VirtioGpuResult1224     fn update_scanout_resource(
1225         &mut self,
1226         scanout_type: SurfaceType,
1227         scanout_id: u32,
1228         scanout_data: Option<VirtioScanoutBlobData>,
1229         resource_id: u32,
1230     ) -> VirtioGpuResult {
1231         let scanout: &mut VirtioGpuScanout;
1232         let mut scanout_parent_surface_id = None;
1233 
1234         match scanout_type {
1235             SurfaceType::Cursor => {
1236                 let parent_scanout_id = scanout_id;
1237 
1238                 scanout_parent_surface_id = self
1239                     .scanouts
1240                     .get(&parent_scanout_id)
1241                     .ok_or(ErrInvalidScanoutId)
1242                     .map(|parent_scanout| parent_scanout.surface_id)?;
1243 
1244                 scanout = &mut self.cursor_scanout;
1245             }
1246             SurfaceType::Scanout => {
1247                 scanout = self
1248                     .scanouts
1249                     .get_mut(&scanout_id)
1250                     .ok_or(ErrInvalidScanoutId)?;
1251             }
1252         };
1253 
1254         // Virtio spec: "The driver can use resource_id = 0 to disable a scanout."
1255         if resource_id == 0 {
1256             // Ignore any initial set_scanout(..., resource_id: 0) calls.
1257             if scanout.resource_id.is_some() {
1258                 scanout.release_surface(&self.display);
1259             }
1260 
1261             scanout.resource_id = None;
1262             return Ok(OkNoData);
1263         }
1264 
1265         let resource = self
1266             .resources
1267             .get_mut(&resource_id)
1268             .ok_or(ErrInvalidResourceId)?;
1269 
1270         // Ensure scanout has a display surface.
1271         match scanout_type {
1272             SurfaceType::Cursor => {
1273                 if let Some(scanout_parent_surface_id) = scanout_parent_surface_id {
1274                     scanout.create_surface(&self.display, Some(scanout_parent_surface_id))?;
1275                 }
1276             }
1277             SurfaceType::Scanout => {
1278                 scanout.create_surface(&self.display, None)?;
1279             }
1280         }
1281 
1282         resource.scanout_data = scanout_data;
1283 
1284         // `resource_id` has already been verified to be non-zero
1285         let resource_id = match NonZeroU32::new(resource_id) {
1286             Some(id) => id,
1287             None => return Ok(OkNoData),
1288         };
1289         scanout.resource_id = Some(resource_id);
1290 
1291         Ok(OkNoData)
1292     }
1293 
suspend(&self) -> anyhow::Result<()>1294     pub fn suspend(&self) -> anyhow::Result<()> {
1295         self.rutabaga
1296             .suspend()
1297             .context("failed to suspend rutabaga")
1298     }
1299 
snapshot(&self) -> anyhow::Result<VirtioGpuSnapshot>1300     pub fn snapshot(&self) -> anyhow::Result<VirtioGpuSnapshot> {
1301         Ok(VirtioGpuSnapshot {
1302             scanouts: self
1303                 .scanouts
1304                 .iter()
1305                 .map(|(i, s)| (*i, s.snapshot()))
1306                 .collect(),
1307             scanouts_updated: self.scanouts_updated.load(Ordering::SeqCst),
1308             cursor_scanout: self.cursor_scanout.snapshot(),
1309             rutabaga: {
1310                 let mut buffer = std::io::Cursor::new(Vec::new());
1311                 self.rutabaga
1312                     .snapshot(&mut buffer, "")
1313                     .context("failed to snapshot rutabaga")?;
1314                 buffer.into_inner()
1315             },
1316             resources: self
1317                 .resources
1318                 .iter()
1319                 .map(|(i, r)| (*i, r.snapshot()))
1320                 .collect(),
1321         })
1322     }
1323 
restore(&mut self, snapshot: VirtioGpuSnapshot) -> anyhow::Result<()>1324     pub fn restore(&mut self, snapshot: VirtioGpuSnapshot) -> anyhow::Result<()> {
1325         self.deferred_snapshot_load = Some(snapshot);
1326         Ok(())
1327     }
1328 
resume(&mut self, mem: &GuestMemory) -> anyhow::Result<()>1329     pub fn resume(&mut self, mem: &GuestMemory) -> anyhow::Result<()> {
1330         if let Some(snapshot) = self.deferred_snapshot_load.take() {
1331             assert!(self.scanouts.keys().eq(snapshot.scanouts.keys()));
1332             for (i, s) in snapshot.scanouts.into_iter() {
1333                 self.scanouts
1334                     .get_mut(&i)
1335                     .unwrap()
1336                     .restore(
1337                         s,
1338                         // Only the cursor scanout can have a parent.
1339                         None,
1340                         &self.display,
1341                     )
1342                     .context("failed to restore scanouts")?;
1343             }
1344             self.scanouts_updated
1345                 .store(snapshot.scanouts_updated, Ordering::SeqCst);
1346 
1347             let cursor_parent_surface_id = snapshot
1348                 .cursor_scanout
1349                 .parent_scanout_id
1350                 .and_then(|i| self.scanouts.get(&i).unwrap().surface_id);
1351             self.cursor_scanout
1352                 .restore(
1353                     snapshot.cursor_scanout,
1354                     cursor_parent_surface_id,
1355                     &self.display,
1356                 )
1357                 .context("failed to restore cursor scanout")?;
1358 
1359             self.rutabaga
1360                 .restore(&mut &snapshot.rutabaga[..], "")
1361                 .context("failed to restore rutabaga")?;
1362 
1363             for (id, s) in snapshot.resources.into_iter() {
1364                 let backing_iovecs = s.backing_iovecs.clone();
1365                 let shmem_offset = s.shmem_offset;
1366                 self.resources.insert(id, VirtioGpuResource::restore(s));
1367                 if let Some(backing_iovecs) = backing_iovecs {
1368                     self.attach_backing(id, mem, backing_iovecs)
1369                         .context("failed to restore resource backing")?;
1370                 }
1371                 if let Some(shmem_offset) = shmem_offset {
1372                     self.resource_map_blob(id, shmem_offset)
1373                         .context("failed to restore resource mapping")?;
1374                 }
1375             }
1376         }
1377 
1378         self.rutabaga.resume().context("failed to resume rutabaga")
1379     }
1380 }
1381