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