1use std::cell::RefCell;
6use std::collections::BTreeMap as Map;
7use std::collections::BTreeSet as Set;
8use std::io::IoSliceMut;
9use std::num::NonZeroU32;
10use std::path::PathBuf;
11use std::rc::Rc;
12use std::result::Result;
13use std::sync::atomic::AtomicBool;
14use std::sync::atomic::Ordering;
15use std::sync::Arc;
16
17use anyhow::Context;
18use base::error;
19use base::FromRawDescriptor;
20use base::IntoRawDescriptor;
21use base::Protection;
22use base::SafeDescriptor;
23use base::VolatileSlice;
24use gpu_display::*;
25use hypervisor::MemCacheType;
26use libc::c_void;
27use rutabaga_gfx::Resource3DInfo;
28use rutabaga_gfx::ResourceCreate3D;
29use rutabaga_gfx::ResourceCreateBlob;
30use rutabaga_gfx::Rutabaga;
31use rutabaga_gfx::RutabagaDescriptor;
32#[cfg(windows)]
33use rutabaga_gfx::RutabagaError;
34use rutabaga_gfx::RutabagaFence;
35use rutabaga_gfx::RutabagaFromRawDescriptor;
36use rutabaga_gfx::RutabagaHandle;
37use rutabaga_gfx::RutabagaIntoRawDescriptor;
38use rutabaga_gfx::RutabagaIovec;
39use rutabaga_gfx::RutabagaMesaHandle;
40#[cfg(windows)]
41use rutabaga_gfx::RutabagaUnsupported;
42use rutabaga_gfx::Transfer3D;
43use rutabaga_gfx::RUTABAGA_HANDLE_TYPE_MEM_DMABUF;
44use rutabaga_gfx::RUTABAGA_HANDLE_TYPE_MEM_OPAQUE_FD;
45use rutabaga_gfx::RUTABAGA_MAP_ACCESS_MASK;
46use rutabaga_gfx::RUTABAGA_MAP_ACCESS_READ;
47use rutabaga_gfx::RUTABAGA_MAP_ACCESS_RW;
48use rutabaga_gfx::RUTABAGA_MAP_ACCESS_WRITE;
49use rutabaga_gfx::RUTABAGA_MAP_CACHE_CACHED;
50use rutabaga_gfx::RUTABAGA_MAP_CACHE_MASK;
51use serde::Deserialize;
52use serde::Serialize;
53use sync::Mutex;
54use vm_control::gpu::DisplayMode;
55use vm_control::gpu::DisplayParameters;
56use vm_control::gpu::GpuControlCommand;
57use vm_control::gpu::GpuControlResult;
58use vm_control::gpu::MouseMode;
59use vm_control::VmMemorySource;
60use vm_memory::udmabuf::UdmabufDriver;
61use vm_memory::udmabuf::UdmabufDriverTrait;
62use vm_memory::GuestAddress;
63use vm_memory::GuestMemory;
64
65use super::protocol::virtio_gpu_rect;
66use super::protocol::GpuResponse;
67use super::protocol::GpuResponse::*;
68use super::protocol::GpuResponsePlaneInfo;
69use super::protocol::VirtioGpuResult;
70use super::protocol::VIRTIO_GPU_BLOB_FLAG_CREATE_GUEST_HANDLE;
71use super::protocol::VIRTIO_GPU_BLOB_FLAG_USE_MAPPABLE;
72use super::protocol::VIRTIO_GPU_BLOB_MEM_HOST3D;
73use super::VirtioScanoutBlobData;
74use crate::virtio::gpu::edid::DisplayInfo;
75use crate::virtio::gpu::edid::EdidBytes;
76use crate::virtio::gpu::snapshot::pack_directory_to_snapshot;
77use crate::virtio::gpu::snapshot::unpack_snapshot_to_directory;
78use crate::virtio::gpu::snapshot::DirectorySnapshot;
79use crate::virtio::gpu::GpuDisplayParameters;
80use crate::virtio::gpu::VIRTIO_GPU_MAX_SCANOUTS;
81use crate::virtio::resource_bridge::BufferInfo;
82use crate::virtio::resource_bridge::PlaneInfo;
83use crate::virtio::resource_bridge::ResourceInfo;
84use crate::virtio::resource_bridge::ResourceResponse;
85use crate::virtio::SharedMemoryMapper;
86
87pub fn to_rutabaga_descriptor(s: SafeDescriptor) -> RutabagaDescriptor {
88 unsafe { RutabagaDescriptor::from_raw_descriptor(s.into_raw_descriptor()) }
91}
92
93fn to_safe_descriptor(r: RutabagaDescriptor) -> SafeDescriptor {
94 unsafe { SafeDescriptor::from_raw_descriptor(r.into_raw_descriptor()) }
97}
98
99struct VirtioGpuResource {
100 resource_id: u32,
101 width: u32,
102 height: u32,
103 size: u64,
104 shmem_offset: Option<u64>,
105 scanout_data: Option<VirtioScanoutBlobData>,
106 display_import: Option<u32>,
107 rutabaga_external_mapping: bool,
108 guest_cpu_mappable: bool,
109
110 backing_iovecs: Option<Vec<(GuestAddress, usize)>>,
113}
114
115#[derive(Serialize, Deserialize)]
116struct VirtioGpuResourceSnapshot {
117 resource_id: u32,
118 width: u32,
119 height: u32,
120 size: u64,
121
122 backing_iovecs: Option<Vec<(GuestAddress, usize)>>,
123 shmem_offset: Option<u64>,
124 scanout_data: Option<VirtioScanoutBlobData>,
125 guest_cpu_mappable: bool,
126}
127
128impl VirtioGpuResource {
129 pub fn new(
132 resource_id: u32,
133 width: u32,
134 height: u32,
135 size: u64,
136 guest_cpu_mappable: bool,
137 ) -> VirtioGpuResource {
138 VirtioGpuResource {
139 resource_id,
140 width,
141 height,
142 size,
143 shmem_offset: None,
144 scanout_data: None,
145 display_import: None,
146 rutabaga_external_mapping: false,
147 guest_cpu_mappable,
148 backing_iovecs: None,
149 }
150 }
151
152 fn snapshot(&self) -> VirtioGpuResourceSnapshot {
153 assert!(self.display_import.is_none());
155
156 VirtioGpuResourceSnapshot {
157 resource_id: self.resource_id,
158 width: self.width,
159 height: self.height,
160 size: self.size,
161 backing_iovecs: self.backing_iovecs.clone(),
162 shmem_offset: self.shmem_offset,
163 scanout_data: self.scanout_data,
164 guest_cpu_mappable: self.guest_cpu_mappable,
165 }
166 }
167
168 fn restore(s: VirtioGpuResourceSnapshot) -> Self {
169 let mut resource = VirtioGpuResource::new(
170 s.resource_id,
171 s.width,
172 s.height,
173 s.size,
174 s.guest_cpu_mappable,
175 );
176 resource.backing_iovecs = s.backing_iovecs;
177 resource.scanout_data = s.scanout_data;
178 resource
179 }
180}
181
182struct VirtioGpuScanout {
183 width: u32,
184 height: u32,
185 scanout_type: SurfaceType,
186 scanout_id: Option<u32>,
188 display_params: Option<GpuDisplayParameters>,
190 parent_surface_id: Option<u32>,
192
193 surface_id: Option<u32>,
194 parent_scanout_id: Option<u32>,
195
196 resource_id: Option<NonZeroU32>,
197 position: Option<(u32, u32)>,
198}
199
200#[derive(Serialize, Deserialize)]
201struct VirtioGpuScanoutSnapshot {
202 width: u32,
203 height: u32,
204 scanout_type: SurfaceType,
205 scanout_id: Option<u32>,
206 display_params: Option<GpuDisplayParameters>,
207
208 has_surface: bool,
212 parent_scanout_id: Option<u32>,
213
214 resource_id: Option<NonZeroU32>,
215 position: Option<(u32, u32)>,
216}
217
218impl VirtioGpuScanout {
219 fn new_primary(scanout_id: u32, params: GpuDisplayParameters) -> VirtioGpuScanout {
220 let (width, height) = params.get_virtual_display_size();
221 VirtioGpuScanout {
222 width,
223 height,
224 scanout_type: SurfaceType::Scanout,
225 scanout_id: Some(scanout_id),
226 display_params: Some(params),
227 parent_surface_id: None,
228 surface_id: None,
229 parent_scanout_id: None,
230 resource_id: None,
231 position: None,
232 }
233 }
234
235 fn new_cursor() -> VirtioGpuScanout {
236 VirtioGpuScanout {
239 width: 64,
240 height: 64,
241 scanout_type: SurfaceType::Cursor,
242 scanout_id: None,
243 display_params: None,
244 parent_surface_id: None,
245 surface_id: None,
246 parent_scanout_id: None,
247 resource_id: None,
248 position: None,
249 }
250 }
251
252 fn snapshot(&self) -> VirtioGpuScanoutSnapshot {
253 VirtioGpuScanoutSnapshot {
254 width: self.width,
255 height: self.height,
256 has_surface: self.surface_id.is_some(),
257 resource_id: self.resource_id,
258 scanout_type: self.scanout_type,
259 scanout_id: self.scanout_id,
260 display_params: self.display_params.clone(),
261 parent_scanout_id: self.parent_scanout_id,
262 position: self.position,
263 }
264 }
265
266 fn restore(
267 &mut self,
268 snapshot: VirtioGpuScanoutSnapshot,
269 parent_surface_id: Option<u32>,
270 display: &Rc<RefCell<GpuDisplay>>,
271 ) -> VirtioGpuResult {
272 assert_eq!(self.width, snapshot.width);
276 assert_eq!(self.height, snapshot.height);
277 assert_eq!(self.scanout_type, snapshot.scanout_type);
278 assert_eq!(self.scanout_id, snapshot.scanout_id);
279 assert_eq!(self.display_params, snapshot.display_params);
280
281 self.resource_id = snapshot.resource_id;
282 if snapshot.has_surface {
283 self.create_surface(display, parent_surface_id, None)?;
284 } else {
285 self.release_surface(display);
286 }
287 if let Some((x, y)) = snapshot.position {
288 self.set_position(display, x, y)?;
289 }
290
291 Ok(OkNoData)
292 }
293
294 fn create_surface(
295 &mut self,
296 display: &Rc<RefCell<GpuDisplay>>,
297 new_parent_surface_id: Option<u32>,
298 new_scanout_rect: Option<virtio_gpu_rect>,
299 ) -> VirtioGpuResult {
300 let mut need_to_create = false;
301
302 if self.surface_id.is_none() {
303 need_to_create = true;
304 }
305
306 if self.parent_surface_id != new_parent_surface_id {
307 self.parent_surface_id = new_parent_surface_id;
308 need_to_create = true;
309 }
310
311 if let Some(new_scanout_rect) = new_scanout_rect {
312 let new_width = new_scanout_rect.width.to_native();
321 let new_height = new_scanout_rect.height.to_native();
322 if !(self.width == new_width && self.height == new_height) {
323 self.width = new_width;
324 self.height = new_height;
325 need_to_create = true;
326 }
327 }
328
329 if !need_to_create {
330 return Ok(OkNoData);
331 }
332
333 self.release_surface(display);
334
335 let mut display = display.borrow_mut();
336
337 let display_params = match self.display_params.clone() {
338 Some(mut params) => {
339 params.mode = DisplayMode::Windowed(self.width, self.height);
343 params
344 }
345 None => {
346 DisplayParameters::default_with_mode(DisplayMode::Windowed(self.width, self.height))
347 }
348 };
349 let surface_id = display.create_surface(
350 self.parent_surface_id,
351 self.scanout_id,
352 &display_params,
353 self.scanout_type,
354 )?;
355
356 self.surface_id = Some(surface_id);
357
358 Ok(OkNoData)
359 }
360
361 fn release_surface(&mut self, display: &Rc<RefCell<GpuDisplay>>) {
362 if let Some(surface_id) = self.surface_id {
363 display.borrow_mut().release_surface(surface_id);
364 }
365
366 self.surface_id = None;
367 }
368
369 fn set_mouse_mode(
370 &mut self,
371 display: &Rc<RefCell<GpuDisplay>>,
372 mouse_mode: MouseMode,
373 ) -> VirtioGpuResult {
374 if let Some(surface_id) = self.surface_id {
375 display
376 .borrow_mut()
377 .set_mouse_mode(surface_id, mouse_mode)?;
378 }
379 Ok(OkNoData)
380 }
381
382 fn set_position(
383 &mut self,
384 display: &Rc<RefCell<GpuDisplay>>,
385 x: u32,
386 y: u32,
387 ) -> VirtioGpuResult {
388 if let Some(surface_id) = self.surface_id {
389 display.borrow_mut().set_position(surface_id, x, y)?;
390 self.position = Some((x, y));
391 }
392 Ok(OkNoData)
393 }
394
395 fn commit(&self, display: &Rc<RefCell<GpuDisplay>>) -> VirtioGpuResult {
396 if let Some(surface_id) = self.surface_id {
397 display.borrow_mut().commit(surface_id)?;
398 }
399 Ok(OkNoData)
400 }
401
402 fn flush(
403 &mut self,
404 display: &Rc<RefCell<GpuDisplay>>,
405 resource: &mut VirtioGpuResource,
406 rutabaga: &mut Rutabaga,
407 ) -> VirtioGpuResult {
408 let surface_id = match self.surface_id {
409 Some(id) => id,
410 _ => return Ok(OkNoData),
411 };
412
413 if let Some(import_id) =
414 VirtioGpuScanout::import_resource_to_display(display, surface_id, resource, rutabaga)
415 {
416 display
417 .borrow_mut()
418 .flip_to(surface_id, import_id, None, None, None)
419 .map_err(|e| {
420 error!("flip_to failed: {:#}", e);
421 ErrUnspec
422 })?;
423 return Ok(OkNoData);
424 }
425
426 let mut display = display.borrow_mut();
428
429 if display.next_buffer_in_use(surface_id) {
431 return Ok(OkNoData);
432 }
433
434 let fb = display
435 .framebuffer_region(surface_id, 0, 0, self.width, self.height)
436 .ok_or(ErrUnspec)?;
437
438 let mut transfer = Transfer3D::new_2d(0, 0, self.width, self.height, 0);
439 transfer.stride = fb.stride();
440 let fb_slice = fb.as_volatile_slice();
441 let buf = IoSliceMut::new(
442 unsafe { std::slice::from_raw_parts_mut(fb_slice.as_mut_ptr(), fb_slice.size()) },
444 );
445 rutabaga.transfer_read(0, resource.resource_id, transfer, Some(buf))?;
446
447 display.flip(surface_id);
448 Ok(OkNoData)
449 }
450
451 fn import_resource_to_display(
452 display: &Rc<RefCell<GpuDisplay>>,
453 surface_id: u32,
454 resource: &mut VirtioGpuResource,
455 rutabaga: &mut Rutabaga,
456 ) -> Option<u32> {
457 if let Some(import_id) = resource.display_import {
458 return Some(import_id);
459 }
460 let blob = rutabaga.export_blob(resource.resource_id).ok()?;
461
462 let handle = match blob {
463 RutabagaHandle::AhbInfo(info) => {
464 let import_id = display
465 .borrow_mut()
466 .import_resource(
467 surface_id,
468 DisplayExternalResourceImport::AHardwareBuffer { info },
469 )
470 .ok()?;
471 resource.display_import = Some(import_id);
472 return Some(import_id);
473 }
474 other => RutabagaMesaHandle::try_from(other).ok()?,
475 };
476 let dmabuf = to_safe_descriptor(handle.os_handle);
477
478 let (width, height, format, stride, offset, modifier) = match resource.scanout_data {
479 Some(data) => (
480 data.width,
481 data.height,
482 data.drm_format,
483 data.strides[0],
484 data.offsets[0],
485 0,
486 ),
487 None => {
488 let query = rutabaga.resource3d_info(resource.resource_id).ok()?;
489 (
490 resource.width,
491 resource.height,
492 query.drm_fourcc,
493 query.strides[0],
494 query.offsets[0],
495 query.modifier,
496 )
497 }
498 };
499
500 let import_id = display
501 .borrow_mut()
502 .import_resource(
503 surface_id,
504 DisplayExternalResourceImport::Dmabuf {
505 descriptor: &dmabuf,
506 offset,
507 stride,
508 modifiers: modifier,
509 width,
510 height,
511 fourcc: format,
512 },
513 )
514 .ok()?;
515 resource.display_import = Some(import_id);
516 Some(import_id)
517 }
518}
519
520pub struct VirtioGpu {
522 display: Rc<RefCell<GpuDisplay>>,
523 scanouts: Map<u32, VirtioGpuScanout>,
524 scanouts_updated: Arc<AtomicBool>,
525 cursor_scanout: VirtioGpuScanout,
526 mapper: Arc<Mutex<Option<Box<dyn SharedMemoryMapper>>>>,
527 rutabaga: Rutabaga,
528 resources: Map<u32, VirtioGpuResource>,
529 external_blob: bool,
530 fixed_blob_mapping: bool,
531 udmabuf_driver: Option<UdmabufDriver>,
532 snapshot_scratch_directory: Option<PathBuf>,
533 deferred_snapshot_load: Option<VirtioGpuSnapshot>,
534}
535
536#[derive(Serialize, Deserialize)]
548pub struct VirtioGpuSnapshot {
549 scanouts: Map<u32, VirtioGpuScanoutSnapshot>,
550 scanouts_updated: bool,
551 cursor_scanout: VirtioGpuScanoutSnapshot,
552 rutabaga: DirectorySnapshot,
553 resources: Map<u32, VirtioGpuResourceSnapshot>,
554}
555
556#[derive(Serialize, Deserialize)]
557struct RutabagaResourceSnapshotSerializable {
558 resource_id: u32,
559
560 width: u32,
561 height: u32,
562 host_mem_size: usize,
563
564 backing_iovecs: Option<Vec<(GuestAddress, usize)>>,
565 component_mask: u8,
566 size: u64,
567}
568
569fn sglist_to_rutabaga_iovecs(
570 vecs: &[(GuestAddress, usize)],
571 mem: &GuestMemory,
572) -> Result<Vec<RutabagaIovec>, ()> {
573 if vecs
574 .iter()
575 .any(|&(addr, len)| mem.get_slice_at_addr(addr, len).is_err())
576 {
577 return Err(());
578 }
579
580 let mut rutabaga_iovecs: Vec<RutabagaIovec> = Vec::new();
581 for &(addr, len) in vecs {
582 let slice = mem.get_slice_at_addr(addr, len).unwrap();
583 rutabaga_iovecs.push(RutabagaIovec {
584 base: slice.as_mut_ptr() as *mut c_void,
585 len,
586 });
587 }
588 Ok(rutabaga_iovecs)
589}
590
591pub enum ProcessDisplayResult {
592 Success,
593 CloseRequested,
594 Error(GpuDisplayError),
595}
596
597impl VirtioGpu {
598 pub fn new(
600 display: GpuDisplay,
601 display_params: Vec<GpuDisplayParameters>,
602 display_event: Arc<AtomicBool>,
603 rutabaga: Rutabaga,
604 mapper: Arc<Mutex<Option<Box<dyn SharedMemoryMapper>>>>,
605 external_blob: bool,
606 fixed_blob_mapping: bool,
607 udmabuf: bool,
608 snapshot_scratch_directory: Option<PathBuf>,
609 ) -> Option<VirtioGpu> {
610 let mut udmabuf_driver = None;
611 if udmabuf {
612 udmabuf_driver = Some(
613 UdmabufDriver::new()
614 .map_err(|e| error!("failed to initialize udmabuf: {}", e))
615 .ok()?,
616 );
617 }
618
619 let scanouts = display_params
620 .iter()
621 .enumerate()
622 .map(|(display_index, display_param)| {
623 (
624 display_index as u32,
625 VirtioGpuScanout::new_primary(display_index as u32, display_param.clone()),
626 )
627 })
628 .collect::<Map<_, _>>();
629 let cursor_scanout = VirtioGpuScanout::new_cursor();
630
631 Some(VirtioGpu {
632 display: Rc::new(RefCell::new(display)),
633 scanouts,
634 scanouts_updated: display_event,
635 cursor_scanout,
636 mapper,
637 rutabaga,
638 resources: Default::default(),
639 external_blob,
640 fixed_blob_mapping,
641 udmabuf_driver,
642 deferred_snapshot_load: None,
643 snapshot_scratch_directory,
644 })
645 }
646
647 pub fn import_event_device(&mut self, event_device: EventDevice) -> VirtioGpuResult {
649 let mut display = self.display.borrow_mut();
650 let _event_device_id = display.import_event_device(event_device)?;
651 Ok(OkNoData)
652 }
653
654 pub fn display(&mut self) -> &Rc<RefCell<GpuDisplay>> {
656 &self.display
657 }
658
659 pub fn display_info(&self) -> Vec<(u32, u32, bool)> {
662 (0..VIRTIO_GPU_MAX_SCANOUTS)
663 .map(|scanout_id| scanout_id as u32)
664 .map(|scanout_id| {
665 self.scanouts
666 .get(&scanout_id)
667 .map_or((0, 0, false), |scanout| {
668 (scanout.width, scanout.height, true)
669 })
670 })
671 .collect::<Vec<_>>()
672 }
673
674 fn add_displays(&mut self, displays: Vec<DisplayParameters>) -> GpuControlResult {
676 let requested_num_scanouts = self.scanouts.len() + displays.len();
677 if requested_num_scanouts > VIRTIO_GPU_MAX_SCANOUTS {
678 return GpuControlResult::TooManyDisplays {
679 allowed: VIRTIO_GPU_MAX_SCANOUTS,
680 requested: requested_num_scanouts,
681 };
682 }
683
684 let mut available_scanout_ids = (0..VIRTIO_GPU_MAX_SCANOUTS)
685 .map(|s| s as u32)
686 .collect::<Set<u32>>();
687
688 self.scanouts.keys().for_each(|scanout_id| {
689 available_scanout_ids.remove(scanout_id);
690 });
691
692 for display_params in displays.into_iter() {
693 let new_scanout_id = *available_scanout_ids.iter().next().unwrap();
694 available_scanout_ids.remove(&new_scanout_id);
695
696 self.scanouts.insert(
697 new_scanout_id,
698 VirtioGpuScanout::new_primary(new_scanout_id, display_params),
699 );
700 }
701
702 self.scanouts_updated.store(true, Ordering::Relaxed);
703
704 GpuControlResult::DisplaysUpdated
705 }
706
707 fn list_displays(&self) -> GpuControlResult {
709 GpuControlResult::DisplayList {
710 displays: self
711 .scanouts
712 .iter()
713 .filter_map(|(scanout_id, scanout)| {
714 scanout
715 .display_params
716 .as_ref()
717 .cloned()
718 .map(|display_params| (*scanout_id, display_params))
719 })
720 .collect(),
721 }
722 }
723
724 fn remove_displays(&mut self, display_ids: Vec<u32>) -> GpuControlResult {
726 for display_id in display_ids {
727 if let Some(mut scanout) = self.scanouts.remove(&display_id) {
728 scanout.release_surface(&self.display);
729 } else {
730 return GpuControlResult::NoSuchDisplay { display_id };
731 }
732 }
733
734 self.scanouts_updated.store(true, Ordering::Relaxed);
735 GpuControlResult::DisplaysUpdated
736 }
737
738 fn set_display_mouse_mode(
739 &mut self,
740 display_id: u32,
741 mouse_mode: MouseMode,
742 ) -> GpuControlResult {
743 match self.scanouts.get_mut(&display_id) {
744 Some(scanout) => match scanout.set_mouse_mode(&self.display, mouse_mode) {
745 Ok(_) => GpuControlResult::DisplayMouseModeSet,
746 Err(e) => GpuControlResult::ErrString(e.to_string()),
747 },
748 None => GpuControlResult::NoSuchDisplay { display_id },
749 }
750 }
751
752 pub fn process_gpu_control_command(&mut self, cmd: GpuControlCommand) -> GpuControlResult {
754 match cmd {
755 GpuControlCommand::AddDisplays { displays } => self.add_displays(displays),
756 GpuControlCommand::ListDisplays => self.list_displays(),
757 GpuControlCommand::RemoveDisplays { display_ids } => self.remove_displays(display_ids),
758 GpuControlCommand::SetDisplayMouseMode {
759 display_id,
760 mouse_mode,
761 } => self.set_display_mouse_mode(display_id, mouse_mode),
762 }
763 }
764
765 pub fn process_display(&mut self) -> ProcessDisplayResult {
767 let mut display = self.display.borrow_mut();
768 let result = display.dispatch_events();
769 match result {
770 Ok(_) => (),
771 Err(e) => {
772 error!("failed to dispatch events: {}", e);
773 return ProcessDisplayResult::Error(e);
774 }
775 }
776
777 for scanout in self.scanouts.values() {
778 let close_requested = scanout
779 .surface_id
780 .map(|surface_id| display.close_requested(surface_id))
781 .unwrap_or(false);
782
783 if close_requested {
784 return ProcessDisplayResult::CloseRequested;
785 }
786 }
787
788 ProcessDisplayResult::Success
789 }
790
791 pub fn set_scanout(
793 &mut self,
794 scanout_rect: virtio_gpu_rect,
795 scanout_id: u32,
796 resource_id: u32,
797 scanout_data: Option<VirtioScanoutBlobData>,
798 ) -> VirtioGpuResult {
799 self.update_scanout_resource(
800 SurfaceType::Scanout,
801 Some(scanout_rect),
802 scanout_id,
803 scanout_data,
804 resource_id,
805 )
806 }
807
808 pub fn flush_resource(&mut self, resource_id: u32) -> VirtioGpuResult {
810 if resource_id == 0 {
811 return Ok(OkNoData);
812 }
813
814 #[cfg(windows)]
815 match self.rutabaga.resource_flush(resource_id) {
816 Ok(_) => return Ok(OkNoData),
817 Err(RutabagaError::MesaError(RutabagaUnsupported)) => {}
818 Err(e) => return Err(ErrRutabaga(e)),
819 }
820
821 let resource = self
822 .resources
823 .get_mut(&resource_id)
824 .ok_or(ErrInvalidResourceId)?;
825
826 let resource_id = match NonZeroU32::new(resource_id) {
828 Some(id) => Some(id),
829 None => return Ok(OkNoData),
830 };
831
832 for scanout in self.scanouts.values_mut() {
833 if scanout.resource_id == resource_id {
834 scanout.flush(&self.display, resource, &mut self.rutabaga)?;
835 }
836 }
837 if self.cursor_scanout.resource_id == resource_id {
838 self.cursor_scanout
839 .flush(&self.display, resource, &mut self.rutabaga)?;
840 }
841
842 Ok(OkNoData)
843 }
844
845 pub fn update_cursor(
848 &mut self,
849 resource_id: u32,
850 scanout_id: u32,
851 x: u32,
852 y: u32,
853 ) -> VirtioGpuResult {
854 self.update_scanout_resource(SurfaceType::Cursor, None, scanout_id, None, resource_id)?;
855
856 self.cursor_scanout.set_position(&self.display, x, y)?;
857
858 self.flush_resource(resource_id)
859 }
860
861 pub fn move_cursor(&mut self, _scanout_id: u32, x: u32, y: u32) -> VirtioGpuResult {
863 self.cursor_scanout.set_position(&self.display, x, y)?;
864 self.cursor_scanout.commit(&self.display)?;
865 Ok(OkNoData)
866 }
867
868 pub fn resource_assign_uuid(&self, resource_id: u32) -> VirtioGpuResult {
870 if !self.resources.contains_key(&resource_id) {
871 return Err(ErrInvalidResourceId);
872 }
873
874 let mut uuid: [u8; 16] = [0; 16];
878 for (idx, byte) in resource_id.to_be_bytes().iter().enumerate() {
879 uuid[12 + idx] = *byte;
880 }
881 Ok(OkResourceUuid { uuid })
882 }
883
884 pub fn export_resource(&mut self, resource_id: u32) -> ResourceResponse {
886 let handle = match self.rutabaga.export_blob(resource_id) {
887 Ok(handle) => {
888 let Ok(handle) = RutabagaMesaHandle::try_from(handle) else {
889 return ResourceResponse::Invalid;
890 };
891 to_safe_descriptor(handle.os_handle)
892 }
893 Err(_) => return ResourceResponse::Invalid,
894 };
895
896 let q = match self.rutabaga.resource3d_info(resource_id) {
897 Ok(query) => query,
898 Err(_) => return ResourceResponse::Invalid,
899 };
900
901 let guest_cpu_mappable = self
904 .resources
905 .get(&resource_id)
906 .map(|r| r.guest_cpu_mappable)
907 .unwrap_or(false);
908
909 ResourceResponse::Resource(ResourceInfo::Buffer(BufferInfo {
910 handle,
911 planes: [
912 PlaneInfo {
913 offset: q.offsets[0],
914 stride: q.strides[0],
915 },
916 PlaneInfo {
917 offset: q.offsets[1],
918 stride: q.strides[1],
919 },
920 PlaneInfo {
921 offset: q.offsets[2],
922 stride: q.strides[2],
923 },
924 PlaneInfo {
925 offset: q.offsets[3],
926 stride: q.strides[3],
927 },
928 ],
929 modifier: q.modifier,
930 guest_cpu_mappable,
931 }))
932 }
933
934 pub fn export_fence(&mut self, fence_id: u64) -> ResourceResponse {
936 match self.rutabaga.export_fence(fence_id) {
937 Ok(handle) => ResourceResponse::Resource(ResourceInfo::Fence {
938 handle: to_safe_descriptor(handle.os_handle),
939 }),
940 Err(_) => ResourceResponse::Invalid,
941 }
942 }
943
944 pub fn get_capset_info(&self, index: u32) -> VirtioGpuResult {
946 if let Ok((capset_id, version, size)) = self.rutabaga.get_capset_info(index) {
947 Ok(OkCapsetInfo {
948 capset_id,
949 version,
950 size,
951 })
952 } else {
953 base::warn!(
956 "virtio-gpu get_capset_info(index={}) failed. intentionally poisoning response",
957 index
958 );
959 Ok(OkCapsetInfo {
960 capset_id: u32::MAX,
961 version: 0,
962 size: 0,
963 })
964 }
965 }
966
967 pub fn get_capset(&self, capset_id: u32, version: u32) -> VirtioGpuResult {
969 let capset = self.rutabaga.get_capset(capset_id, version)?;
970 Ok(OkCapset(capset))
971 }
972
973 pub fn force_ctx_0(&self) {
975 self.rutabaga.force_ctx_0()
976 }
977
978 pub fn create_fence(&mut self, rutabaga_fence: RutabagaFence) -> VirtioGpuResult {
981 self.rutabaga.create_fence(rutabaga_fence)?;
982 Ok(OkNoData)
983 }
984
985 pub fn event_poll(&self) {
987 self.rutabaga.event_poll();
988 }
989
990 pub fn poll_descriptor(&self) -> Option<SafeDescriptor> {
993 self.rutabaga.poll_descriptor().map(to_safe_descriptor)
994 }
995
996 pub fn resource_create_3d(
998 &mut self,
999 resource_id: u32,
1000 resource_create_3d: ResourceCreate3D,
1001 ) -> VirtioGpuResult {
1002 self.rutabaga
1003 .resource_create_3d(resource_id, resource_create_3d)?;
1004
1005 let resource = VirtioGpuResource::new(
1006 resource_id,
1007 resource_create_3d.width,
1008 resource_create_3d.height,
1009 0,
1010 false,
1011 );
1012
1013 self.resources.insert(resource_id, resource);
1015 Ok(self.result_from_query(resource_id))
1016 }
1017
1018 pub fn attach_backing(
1022 &mut self,
1023 resource_id: u32,
1024 mem: &GuestMemory,
1025 vecs: Vec<(GuestAddress, usize)>,
1026 ) -> VirtioGpuResult {
1027 let resource = self
1028 .resources
1029 .get_mut(&resource_id)
1030 .ok_or(ErrInvalidResourceId)?;
1031
1032 let rutabaga_iovecs = sglist_to_rutabaga_iovecs(&vecs[..], mem).map_err(|_| ErrUnspec)?;
1033 self.rutabaga.attach_backing(resource_id, rutabaga_iovecs)?;
1034 resource.backing_iovecs = Some(vecs);
1035 Ok(OkNoData)
1036 }
1037
1038 pub fn detach_backing(&mut self, resource_id: u32) -> VirtioGpuResult {
1040 let resource = self
1041 .resources
1042 .get_mut(&resource_id)
1043 .ok_or(ErrInvalidResourceId)?;
1044
1045 self.rutabaga.detach_backing(resource_id)?;
1046 resource.backing_iovecs = None;
1047 Ok(OkNoData)
1048 }
1049
1050 pub fn unref_resource(&mut self, resource_id: u32) -> VirtioGpuResult {
1052 let resource = self
1053 .resources
1054 .remove(&resource_id)
1055 .ok_or(ErrInvalidResourceId)?;
1056
1057 if resource.rutabaga_external_mapping {
1058 self.rutabaga.unmap(resource_id)?;
1059 }
1060
1061 self.rutabaga.unref_resource(resource_id)?;
1062 Ok(OkNoData)
1063 }
1064
1065 pub fn transfer_write(
1067 &mut self,
1068 ctx_id: u32,
1069 resource_id: u32,
1070 transfer: Transfer3D,
1071 ) -> VirtioGpuResult {
1072 self.rutabaga
1073 .transfer_write(ctx_id, resource_id, transfer, None)?;
1074 Ok(OkNoData)
1075 }
1076
1077 pub fn transfer_read(
1083 &mut self,
1084 ctx_id: u32,
1085 resource_id: u32,
1086 transfer: Transfer3D,
1087 buf: Option<VolatileSlice>,
1088 ) -> VirtioGpuResult {
1089 let buf = buf.map(|vs| {
1090 IoSliceMut::new(
1091 unsafe { std::slice::from_raw_parts_mut(vs.as_mut_ptr(), vs.size()) },
1093 )
1094 });
1095 self.rutabaga
1096 .transfer_read(ctx_id, resource_id, transfer, buf)?;
1097 Ok(OkNoData)
1098 }
1099
1100 pub fn resource_create_blob(
1102 &mut self,
1103 ctx_id: u32,
1104 resource_id: u32,
1105 resource_create_blob: ResourceCreateBlob,
1106 vecs: Vec<(GuestAddress, usize)>,
1107 mem: &GuestMemory,
1108 ) -> VirtioGpuResult {
1109 let mut descriptor = None;
1110 let mut rutabaga_iovecs = None;
1111
1112 if resource_create_blob.blob_flags & VIRTIO_GPU_BLOB_FLAG_CREATE_GUEST_HANDLE != 0 {
1113 descriptor = match self.udmabuf_driver {
1114 Some(ref driver) => Some(driver.create_udmabuf(mem, &vecs[..])?),
1115 None => return Err(ErrUnspec),
1116 }
1117 } else if resource_create_blob.blob_mem != VIRTIO_GPU_BLOB_MEM_HOST3D {
1118 rutabaga_iovecs =
1119 Some(sglist_to_rutabaga_iovecs(&vecs[..], mem).map_err(|_| ErrUnspec)?);
1120 }
1121
1122 self.rutabaga.resource_create_blob(
1123 ctx_id,
1124 resource_id,
1125 resource_create_blob,
1126 rutabaga_iovecs,
1127 descriptor.map(|descriptor| {
1128 RutabagaMesaHandle {
1129 os_handle: to_rutabaga_descriptor(descriptor),
1130 handle_type: RUTABAGA_HANDLE_TYPE_MEM_DMABUF,
1131 }
1132 .into()
1133 }),
1134 )?;
1135
1136 let guest_cpu_mappable =
1137 (resource_create_blob.blob_flags & VIRTIO_GPU_BLOB_FLAG_USE_MAPPABLE) != 0;
1138 let resource = VirtioGpuResource::new(
1139 resource_id,
1140 0,
1141 0,
1142 resource_create_blob.size,
1143 guest_cpu_mappable,
1144 );
1145
1146 self.resources.insert(resource_id, resource);
1148 Ok(self.result_from_query(resource_id))
1149 }
1150
1151 pub fn resource_map_blob(
1158 &mut self,
1159 resource_id: u32,
1160 offset: u64,
1161 ) -> anyhow::Result<GpuResponse> {
1162 let resource = self
1163 .resources
1164 .get_mut(&resource_id)
1165 .with_context(|| format!("can't find the resource with id {resource_id}"))
1166 .context(ErrInvalidResourceId)?;
1167
1168 let map_info = self
1169 .rutabaga
1170 .map_info(resource_id)
1171 .context("failed to retrieve the map info for the resource")
1172 .context(ErrUnspec)?;
1173
1174 let mut source: Option<VmMemorySource> = None;
1175 if let Ok(export) = self.rutabaga.export_blob(resource_id) {
1176 let export = RutabagaMesaHandle::try_from(export)
1177 .context("failed to retrieve the handle info")
1178 .context(ErrUnspec)?;
1179 if let Ok(vulkan_info) = self.rutabaga.vulkan_info(resource_id) {
1180 source = Some(VmMemorySource::Vulkan {
1181 descriptor: to_safe_descriptor(export.os_handle),
1182 handle_type: export.handle_type,
1183 memory_idx: vulkan_info.memory_idx,
1184 device_uuid: vulkan_info.device_id.device_uuid,
1185 driver_uuid: vulkan_info.device_id.driver_uuid,
1186 size: resource.size,
1187 });
1188 } else if export.handle_type != RUTABAGA_HANDLE_TYPE_MEM_OPAQUE_FD {
1189 source = Some(VmMemorySource::Descriptor {
1190 descriptor: to_safe_descriptor(export.os_handle),
1191 offset: 0,
1192 size: resource.size,
1193 });
1194 }
1195 }
1196
1197 if source.is_none() {
1200 anyhow::ensure!(
1201 !self.external_blob,
1202 "can't fallback to external mapping with external blob enabled"
1203 );
1204 anyhow::ensure!(
1205 !self.fixed_blob_mapping,
1206 "can't fallback to external mapping with fixed blob mapping enabled"
1207 );
1208
1209 let mapping = self.rutabaga.map(resource_id).map_err(|e| {
1210 anyhow::anyhow!("failed to map via rutabaga").context(GpuResponse::ErrRutabaga(e))
1211 })?;
1212 resource.rutabaga_external_mapping = true;
1214 source = Some(VmMemorySource::ExternalMapping {
1215 ptr: mapping.ptr,
1216 size: mapping.size,
1217 });
1218 };
1219
1220 let prot = match map_info & RUTABAGA_MAP_ACCESS_MASK {
1221 RUTABAGA_MAP_ACCESS_READ => Protection::read(),
1222 RUTABAGA_MAP_ACCESS_WRITE => Protection::write(),
1223 RUTABAGA_MAP_ACCESS_RW => Protection::read_write(),
1224 access_flags => {
1225 return Err(anyhow::anyhow!(
1226 "unrecognized access flags {:#x}",
1227 access_flags
1228 ))
1229 .context(ErrUnspec)
1230 }
1231 };
1232
1233 let cache = if cfg!(feature = "noncoherent-dma")
1234 && map_info & RUTABAGA_MAP_CACHE_MASK != RUTABAGA_MAP_CACHE_CACHED
1235 {
1236 MemCacheType::CacheNonCoherent
1237 } else {
1238 MemCacheType::CacheCoherent
1239 };
1240
1241 self.mapper
1242 .lock()
1243 .as_mut()
1244 .expect("No backend request connection found")
1245 .add_mapping(source.unwrap(), offset, prot, cache)
1246 .context("failed to add the memory mapping")
1247 .context(ErrUnspec)?;
1248
1249 resource.shmem_offset = Some(offset);
1250 Ok(OkMapInfo {
1252 map_info: map_info & RUTABAGA_MAP_CACHE_MASK,
1253 })
1254 }
1255
1256 pub fn resource_unmap_blob(&mut self, resource_id: u32) -> VirtioGpuResult {
1258 let resource = self
1259 .resources
1260 .get_mut(&resource_id)
1261 .ok_or(ErrInvalidResourceId)?;
1262
1263 let shmem_offset = resource.shmem_offset.ok_or(ErrUnspec)?;
1264 self.mapper
1265 .lock()
1266 .as_mut()
1267 .expect("No backend request connection found")
1268 .remove_mapping(shmem_offset)
1269 .map_err(|_| ErrUnspec)?;
1270 resource.shmem_offset = None;
1271
1272 if resource.rutabaga_external_mapping {
1273 self.rutabaga.unmap(resource_id)?;
1274 resource.rutabaga_external_mapping = false;
1275 }
1276
1277 Ok(OkNoData)
1278 }
1279
1280 pub fn get_edid(&self, scanout_id: u32) -> VirtioGpuResult {
1283 let display_info = match self.scanouts.get(&scanout_id) {
1284 Some(scanout) => {
1285 let params = scanout.display_params.as_ref().unwrap();
1287 DisplayInfo::new(params)
1288 }
1289 None => DisplayInfo::new(&Default::default()),
1290 };
1291 EdidBytes::new(&display_info)
1292 }
1293
1294 pub fn create_context(
1296 &mut self,
1297 ctx_id: u32,
1298 context_init: u32,
1299 context_name: Option<&str>,
1300 ) -> VirtioGpuResult {
1301 self.rutabaga
1302 .create_context(ctx_id, context_init, context_name)?;
1303 Ok(OkNoData)
1304 }
1305
1306 pub fn destroy_context(&mut self, ctx_id: u32) -> VirtioGpuResult {
1308 self.rutabaga.destroy_context(ctx_id)?;
1309 Ok(OkNoData)
1310 }
1311
1312 pub fn context_attach_resource(&mut self, ctx_id: u32, resource_id: u32) -> VirtioGpuResult {
1314 self.rutabaga.context_attach_resource(ctx_id, resource_id)?;
1315 Ok(OkNoData)
1316 }
1317
1318 pub fn context_detach_resource(&mut self, ctx_id: u32, resource_id: u32) -> VirtioGpuResult {
1320 self.rutabaga.context_detach_resource(ctx_id, resource_id)?;
1321 Ok(OkNoData)
1322 }
1323
1324 pub fn submit_command(
1326 &mut self,
1327 ctx_id: u32,
1328 commands: &mut [u8],
1329 fence_ids: &[u64],
1330 ) -> VirtioGpuResult {
1331 self.rutabaga.submit_command(ctx_id, commands, fence_ids)?;
1332 Ok(OkNoData)
1333 }
1334
1335 fn result_from_query(&mut self, resource_id: u32) -> GpuResponse {
1337 match self.rutabaga.resource3d_info(resource_id) {
1338 Ok(query) => {
1339 let mut plane_info = Vec::with_capacity(4);
1340 for plane_index in 0..4 {
1341 plane_info.push(GpuResponsePlaneInfo {
1342 stride: query.strides[plane_index],
1343 offset: query.offsets[plane_index],
1344 });
1345 }
1346 let format_modifier = query.modifier;
1347 OkResourcePlaneInfo {
1348 format_modifier,
1349 plane_info,
1350 }
1351 }
1352 Err(_) => OkNoData,
1353 }
1354 }
1355
1356 fn update_scanout_resource(
1357 &mut self,
1358 scanout_type: SurfaceType,
1359 scanout_rect: Option<virtio_gpu_rect>,
1360 scanout_id: u32,
1361 scanout_data: Option<VirtioScanoutBlobData>,
1362 resource_id: u32,
1363 ) -> VirtioGpuResult {
1364 let scanout: &mut VirtioGpuScanout;
1365 let mut scanout_parent_surface_id = None;
1366
1367 match scanout_type {
1368 SurfaceType::Cursor => {
1369 let parent_scanout_id = scanout_id;
1370
1371 scanout_parent_surface_id = self
1372 .scanouts
1373 .get(&parent_scanout_id)
1374 .ok_or(ErrInvalidScanoutId)
1375 .map(|parent_scanout| parent_scanout.surface_id)?;
1376
1377 scanout = &mut self.cursor_scanout;
1378 }
1379 SurfaceType::Scanout => {
1380 scanout = self
1381 .scanouts
1382 .get_mut(&scanout_id)
1383 .ok_or(ErrInvalidScanoutId)?;
1384 }
1385 };
1386
1387 if resource_id == 0 {
1389 if scanout.resource_id.is_some() {
1391 scanout.release_surface(&self.display);
1392 }
1393
1394 scanout.resource_id = None;
1395 return Ok(OkNoData);
1396 }
1397
1398 let resource = self
1399 .resources
1400 .get_mut(&resource_id)
1401 .ok_or(ErrInvalidResourceId)?;
1402
1403 match scanout_type {
1405 SurfaceType::Cursor => {
1406 if let Some(scanout_parent_surface_id) = scanout_parent_surface_id {
1407 scanout.create_surface(
1408 &self.display,
1409 Some(scanout_parent_surface_id),
1410 scanout_rect,
1411 )?;
1412 }
1413 }
1414 SurfaceType::Scanout => {
1415 scanout.create_surface(&self.display, None, scanout_rect)?;
1416 }
1417 }
1418
1419 let info = scanout_data.map(|scanout_data| Resource3DInfo {
1420 width: scanout_data.width,
1421 height: scanout_data.height,
1422 drm_fourcc: scanout_data.drm_format,
1423 strides: scanout_data.strides,
1424 offsets: scanout_data.offsets,
1425 modifier: 0,
1426 });
1427
1428 let _ = self.rutabaga.set_scanout(scanout_id, resource_id, info);
1429
1430 resource.scanout_data = scanout_data;
1431
1432 let resource_id = match NonZeroU32::new(resource_id) {
1434 Some(id) => id,
1435 None => return Ok(OkNoData),
1436 };
1437 scanout.resource_id = Some(resource_id);
1438
1439 Ok(OkNoData)
1440 }
1441
1442 pub fn suspend(&self) -> anyhow::Result<()> {
1443 self.rutabaga
1444 .suspend()
1445 .context("failed to suspend rutabaga")
1446 }
1447
1448 pub fn snapshot(&self) -> anyhow::Result<VirtioGpuSnapshot> {
1449 let snapshot_directory_tempdir = if let Some(dir) = &self.snapshot_scratch_directory {
1450 tempfile::tempdir_in(dir).with_context(|| {
1451 format!(
1452 "failed to create tempdir in {} for gpu rutabaga snapshot",
1453 dir.display()
1454 )
1455 })?
1456 } else {
1457 tempfile::tempdir().context("failed to create tempdir for gpu rutabaga snapshot")?
1458 };
1459 let snapshot_directory = snapshot_directory_tempdir.path();
1460
1461 Ok(VirtioGpuSnapshot {
1462 scanouts: self
1463 .scanouts
1464 .iter()
1465 .map(|(i, s)| (*i, s.snapshot()))
1466 .collect(),
1467 scanouts_updated: self.scanouts_updated.load(Ordering::SeqCst),
1468 cursor_scanout: self.cursor_scanout.snapshot(),
1469 rutabaga: {
1470 self.rutabaga
1471 .snapshot(snapshot_directory)
1472 .context("failed to snapshot rutabaga")?;
1473
1474 pack_directory_to_snapshot(snapshot_directory).with_context(|| {
1475 format!(
1476 "failed to pack rutabaga snapshot from {}",
1477 snapshot_directory.display()
1478 )
1479 })?
1480 },
1481 resources: self
1482 .resources
1483 .iter()
1484 .map(|(i, r)| (*i, r.snapshot()))
1485 .collect(),
1486 })
1487 }
1488
1489 pub fn restore(&mut self, snapshot: VirtioGpuSnapshot) -> anyhow::Result<()> {
1490 self.deferred_snapshot_load = Some(snapshot);
1491 Ok(())
1492 }
1493
1494 pub fn resume(&mut self, mem: &GuestMemory) -> anyhow::Result<()> {
1495 if let Some(snapshot) = self.deferred_snapshot_load.take() {
1496 assert!(self.scanouts.keys().eq(snapshot.scanouts.keys()));
1497 for (i, s) in snapshot.scanouts.into_iter() {
1498 self.scanouts
1499 .get_mut(&i)
1500 .unwrap()
1501 .restore(
1502 s,
1503 None,
1505 &self.display,
1506 )
1507 .context("failed to restore scanouts")?;
1508 }
1509 self.scanouts_updated
1510 .store(snapshot.scanouts_updated, Ordering::SeqCst);
1511
1512 let cursor_parent_surface_id = snapshot
1513 .cursor_scanout
1514 .parent_scanout_id
1515 .and_then(|i| self.scanouts.get(&i).unwrap().surface_id);
1516 self.cursor_scanout
1517 .restore(
1518 snapshot.cursor_scanout,
1519 cursor_parent_surface_id,
1520 &self.display,
1521 )
1522 .context("failed to restore cursor scanout")?;
1523
1524 let snapshot_directory_tempdir = if let Some(dir) = &self.snapshot_scratch_directory {
1525 tempfile::tempdir_in(dir).with_context(|| {
1526 format!(
1527 "failed to create tempdir in {} for gpu rutabaga snapshot",
1528 dir.display()
1529 )
1530 })?
1531 } else {
1532 tempfile::tempdir().context("failed to create tempdir for gpu rutabaga snapshot")?
1533 };
1534 let snapshot_directory = snapshot_directory_tempdir.path();
1535
1536 unpack_snapshot_to_directory(snapshot_directory, snapshot.rutabaga).with_context(
1537 || {
1538 format!(
1539 "failed to unpack rutabaga snapshot to {}",
1540 snapshot_directory.display()
1541 )
1542 },
1543 )?;
1544 self.rutabaga
1545 .restore(snapshot_directory)
1546 .context("failed to restore rutabaga")?;
1547
1548 for (id, s) in snapshot.resources.into_iter() {
1549 let backing_iovecs = s.backing_iovecs.clone();
1550 let shmem_offset = s.shmem_offset;
1551 self.resources.insert(id, VirtioGpuResource::restore(s));
1552 if let Some(backing_iovecs) = backing_iovecs {
1553 self.attach_backing(id, mem, backing_iovecs)
1554 .context("failed to restore resource backing")?;
1555 }
1556 if let Some(shmem_offset) = shmem_offset {
1557 self.resource_map_blob(id, shmem_offset)
1558 .context("failed to restore resource mapping")?;
1559 }
1560 }
1561 }
1562
1563 self.rutabaga.resume().context("failed to resume rutabaga")
1564 }
1565}