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