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