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