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