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::RutabagaMagmaHandle;
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 => RutabagaMagmaHandle::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::MagmaGpuError(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) = RutabagaMagmaHandle::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 let Some(shmem_offset) = resource.shmem_offset {
1058 if let Some(mapper) = self.mapper.lock().as_mut() {
1059 if let Err(e) = mapper.remove_mapping(shmem_offset) {
1060 error!("failed to remove memory mapping: {:#}", e);
1061 }
1062 }
1063 }
1064
1065 if resource.rutabaga_external_mapping {
1066 self.rutabaga.unmap(resource_id)?;
1067 }
1068
1069 self.rutabaga.unref_resource(resource_id)?;
1070 Ok(OkNoData)
1071 }
1072
1073 pub fn transfer_write(
1075 &mut self,
1076 ctx_id: u32,
1077 resource_id: u32,
1078 transfer: Transfer3D,
1079 ) -> VirtioGpuResult {
1080 self.rutabaga
1081 .transfer_write(ctx_id, resource_id, transfer, None)?;
1082 Ok(OkNoData)
1083 }
1084
1085 pub fn transfer_read(
1091 &mut self,
1092 ctx_id: u32,
1093 resource_id: u32,
1094 transfer: Transfer3D,
1095 buf: Option<VolatileSlice>,
1096 ) -> VirtioGpuResult {
1097 let buf = buf.map(|vs| {
1098 IoSliceMut::new(
1099 unsafe { std::slice::from_raw_parts_mut(vs.as_mut_ptr(), vs.size()) },
1101 )
1102 });
1103 self.rutabaga
1104 .transfer_read(ctx_id, resource_id, transfer, buf)?;
1105 Ok(OkNoData)
1106 }
1107
1108 pub fn resource_create_blob(
1110 &mut self,
1111 ctx_id: u32,
1112 resource_id: u32,
1113 resource_create_blob: ResourceCreateBlob,
1114 vecs: Vec<(GuestAddress, usize)>,
1115 mem: &GuestMemory,
1116 ) -> VirtioGpuResult {
1117 let mut descriptor = None;
1118 let mut rutabaga_iovecs = None;
1119
1120 if resource_create_blob.blob_flags & VIRTIO_GPU_BLOB_FLAG_CREATE_GUEST_HANDLE != 0 {
1121 descriptor = match self.udmabuf_driver {
1122 Some(ref driver) => Some(driver.create_udmabuf(mem, &vecs[..])?),
1123 None => return Err(ErrUnspec),
1124 }
1125 } else if resource_create_blob.blob_mem != VIRTIO_GPU_BLOB_MEM_HOST3D {
1126 rutabaga_iovecs =
1127 Some(sglist_to_rutabaga_iovecs(&vecs[..], mem).map_err(|_| ErrUnspec)?);
1128 }
1129
1130 self.rutabaga.resource_create_blob(
1131 ctx_id,
1132 resource_id,
1133 resource_create_blob,
1134 rutabaga_iovecs,
1135 descriptor.map(|descriptor| {
1136 RutabagaMagmaHandle {
1137 os_handle: to_rutabaga_descriptor(descriptor),
1138 handle_type: RUTABAGA_HANDLE_TYPE_MEM_DMABUF,
1139 }
1140 .into()
1141 }),
1142 )?;
1143
1144 let guest_cpu_mappable =
1145 (resource_create_blob.blob_flags & VIRTIO_GPU_BLOB_FLAG_USE_MAPPABLE) != 0;
1146 let resource = VirtioGpuResource::new(
1147 resource_id,
1148 0,
1149 0,
1150 resource_create_blob.size,
1151 guest_cpu_mappable,
1152 );
1153
1154 self.resources.insert(resource_id, resource);
1156 Ok(self.result_from_query(resource_id))
1157 }
1158
1159 pub fn resource_map_blob(
1166 &mut self,
1167 resource_id: u32,
1168 offset: u64,
1169 ) -> anyhow::Result<GpuResponse> {
1170 let resource = self
1171 .resources
1172 .get_mut(&resource_id)
1173 .with_context(|| format!("can't find the resource with id {resource_id}"))
1174 .context(ErrInvalidResourceId)?;
1175
1176 let map_info = self
1177 .rutabaga
1178 .map_info(resource_id)
1179 .context("failed to retrieve the map info for the resource")
1180 .context(ErrUnspec)?;
1181
1182 let mut source: Option<VmMemorySource> = None;
1183 if let Ok(export) = self.rutabaga.export_blob(resource_id) {
1184 let export = RutabagaMagmaHandle::try_from(export)
1185 .context("failed to retrieve the handle info")
1186 .context(ErrUnspec)?;
1187 if let Ok(vulkan_info) = self.rutabaga.vulkan_info(resource_id) {
1188 source = Some(VmMemorySource::Vulkan {
1189 descriptor: to_safe_descriptor(export.os_handle),
1190 handle_type: export.handle_type,
1191 memory_idx: vulkan_info.memory_idx,
1192 device_uuid: vulkan_info.device_id.device_uuid,
1193 driver_uuid: vulkan_info.device_id.driver_uuid,
1194 size: resource.size,
1195 });
1196 } else if export.handle_type != RUTABAGA_HANDLE_TYPE_MEM_OPAQUE_FD {
1197 source = Some(VmMemorySource::Descriptor {
1198 descriptor: to_safe_descriptor(export.os_handle),
1199 offset: 0,
1200 size: resource.size,
1201 });
1202 }
1203 }
1204
1205 if source.is_none() {
1208 anyhow::ensure!(
1209 !self.external_blob,
1210 "can't fallback to external mapping with external blob enabled"
1211 );
1212 anyhow::ensure!(
1213 !self.fixed_blob_mapping,
1214 "can't fallback to external mapping with fixed blob mapping enabled"
1215 );
1216
1217 let mapping = self.rutabaga.map(resource_id).map_err(|e| {
1218 anyhow::anyhow!("failed to map via rutabaga").context(GpuResponse::ErrRutabaga(e))
1219 })?;
1220 resource.rutabaga_external_mapping = true;
1222 source = Some(VmMemorySource::ExternalMapping {
1223 ptr: mapping.ptr,
1224 size: mapping.size,
1225 });
1226 };
1227
1228 let prot = match map_info & RUTABAGA_MAP_ACCESS_MASK {
1229 RUTABAGA_MAP_ACCESS_READ => Protection::read(),
1230 RUTABAGA_MAP_ACCESS_WRITE => Protection::write(),
1231 RUTABAGA_MAP_ACCESS_RW => Protection::read_write(),
1232 access_flags => {
1233 return Err(anyhow::anyhow!(
1234 "unrecognized access flags {:#x}",
1235 access_flags
1236 ))
1237 .context(ErrUnspec)
1238 }
1239 };
1240
1241 let cache = if cfg!(feature = "noncoherent-dma")
1242 && map_info & RUTABAGA_MAP_CACHE_MASK != RUTABAGA_MAP_CACHE_CACHED
1243 {
1244 MemCacheType::CacheNonCoherent
1245 } else {
1246 MemCacheType::CacheCoherent
1247 };
1248
1249 self.mapper
1250 .lock()
1251 .as_mut()
1252 .expect("No backend request connection found")
1253 .add_mapping(source.unwrap(), offset, prot, cache)
1254 .context("failed to add the memory mapping")
1255 .context(ErrUnspec)?;
1256
1257 resource.shmem_offset = Some(offset);
1258 Ok(OkMapInfo {
1260 map_info: map_info & RUTABAGA_MAP_CACHE_MASK,
1261 })
1262 }
1263
1264 pub fn resource_unmap_blob(&mut self, resource_id: u32) -> VirtioGpuResult {
1266 let resource = self
1267 .resources
1268 .get_mut(&resource_id)
1269 .ok_or(ErrInvalidResourceId)?;
1270
1271 let shmem_offset = resource.shmem_offset.ok_or(ErrUnspec)?;
1272 self.mapper
1273 .lock()
1274 .as_mut()
1275 .expect("No backend request connection found")
1276 .remove_mapping(shmem_offset)
1277 .map_err(|_| ErrUnspec)?;
1278 resource.shmem_offset = None;
1279
1280 if resource.rutabaga_external_mapping {
1281 self.rutabaga.unmap(resource_id)?;
1282 resource.rutabaga_external_mapping = false;
1283 }
1284
1285 Ok(OkNoData)
1286 }
1287
1288 pub fn get_edid(&self, scanout_id: u32) -> VirtioGpuResult {
1291 let display_info = match self.scanouts.get(&scanout_id) {
1292 Some(scanout) => {
1293 let params = scanout.display_params.as_ref().unwrap();
1295 DisplayInfo::new(params)
1296 }
1297 None => DisplayInfo::new(&Default::default()),
1298 };
1299 EdidBytes::new(&display_info)
1300 }
1301
1302 pub fn create_context(
1304 &mut self,
1305 ctx_id: u32,
1306 context_init: u32,
1307 context_name: Option<&str>,
1308 ) -> VirtioGpuResult {
1309 self.rutabaga
1310 .create_context(ctx_id, context_init, context_name)?;
1311 Ok(OkNoData)
1312 }
1313
1314 pub fn destroy_context(&mut self, ctx_id: u32) -> VirtioGpuResult {
1316 self.rutabaga.destroy_context(ctx_id)?;
1317 Ok(OkNoData)
1318 }
1319
1320 pub fn context_attach_resource(&mut self, ctx_id: u32, resource_id: u32) -> VirtioGpuResult {
1322 self.rutabaga.context_attach_resource(ctx_id, resource_id)?;
1323 Ok(OkNoData)
1324 }
1325
1326 pub fn context_detach_resource(&mut self, ctx_id: u32, resource_id: u32) -> VirtioGpuResult {
1328 self.rutabaga.context_detach_resource(ctx_id, resource_id)?;
1329 Ok(OkNoData)
1330 }
1331
1332 pub fn submit_command(
1334 &mut self,
1335 ctx_id: u32,
1336 commands: &mut [u8],
1337 fence_ids: &[u64],
1338 ) -> VirtioGpuResult {
1339 self.rutabaga.submit_command(ctx_id, commands, fence_ids)?;
1340 Ok(OkNoData)
1341 }
1342
1343 fn result_from_query(&mut self, resource_id: u32) -> GpuResponse {
1345 match self.rutabaga.resource3d_info(resource_id) {
1346 Ok(query) => {
1347 let mut plane_info = Vec::with_capacity(4);
1348 for plane_index in 0..4 {
1349 plane_info.push(GpuResponsePlaneInfo {
1350 stride: query.strides[plane_index],
1351 offset: query.offsets[plane_index],
1352 });
1353 }
1354 let format_modifier = query.modifier;
1355 OkResourcePlaneInfo {
1356 format_modifier,
1357 plane_info,
1358 }
1359 }
1360 Err(_) => OkNoData,
1361 }
1362 }
1363
1364 fn update_scanout_resource(
1365 &mut self,
1366 scanout_type: SurfaceType,
1367 scanout_rect: Option<virtio_gpu_rect>,
1368 scanout_id: u32,
1369 scanout_data: Option<VirtioScanoutBlobData>,
1370 resource_id: u32,
1371 ) -> VirtioGpuResult {
1372 let scanout: &mut VirtioGpuScanout;
1373 let mut scanout_parent_surface_id = None;
1374
1375 match scanout_type {
1376 SurfaceType::Cursor => {
1377 let parent_scanout_id = scanout_id;
1378
1379 scanout_parent_surface_id = self
1380 .scanouts
1381 .get(&parent_scanout_id)
1382 .ok_or(ErrInvalidScanoutId)
1383 .map(|parent_scanout| parent_scanout.surface_id)?;
1384
1385 scanout = &mut self.cursor_scanout;
1386 }
1387 SurfaceType::Scanout => {
1388 scanout = self
1389 .scanouts
1390 .get_mut(&scanout_id)
1391 .ok_or(ErrInvalidScanoutId)?;
1392 }
1393 };
1394
1395 if resource_id == 0 {
1397 if scanout.resource_id.is_some() {
1399 scanout.release_surface(&self.display);
1400 }
1401
1402 scanout.resource_id = None;
1403 return Ok(OkNoData);
1404 }
1405
1406 let resource = self
1407 .resources
1408 .get_mut(&resource_id)
1409 .ok_or(ErrInvalidResourceId)?;
1410
1411 match scanout_type {
1413 SurfaceType::Cursor => {
1414 if let Some(scanout_parent_surface_id) = scanout_parent_surface_id {
1415 scanout.create_surface(
1416 &self.display,
1417 Some(scanout_parent_surface_id),
1418 scanout_rect,
1419 )?;
1420 }
1421 }
1422 SurfaceType::Scanout => {
1423 scanout.create_surface(&self.display, None, scanout_rect)?;
1424 }
1425 }
1426
1427 let info = scanout_data.map(|scanout_data| Resource3DInfo {
1428 width: scanout_data.width,
1429 height: scanout_data.height,
1430 drm_fourcc: scanout_data.drm_format,
1431 strides: scanout_data.strides,
1432 offsets: scanout_data.offsets,
1433 modifier: 0,
1434 });
1435
1436 let _ = self.rutabaga.set_scanout(scanout_id, resource_id, info);
1437
1438 resource.scanout_data = scanout_data;
1439
1440 let resource_id = match NonZeroU32::new(resource_id) {
1442 Some(id) => id,
1443 None => return Ok(OkNoData),
1444 };
1445 scanout.resource_id = Some(resource_id);
1446
1447 Ok(OkNoData)
1448 }
1449
1450 pub fn suspend(&self) -> anyhow::Result<()> {
1451 self.rutabaga
1452 .suspend()
1453 .context("failed to suspend rutabaga")
1454 }
1455
1456 pub fn snapshot(&self) -> anyhow::Result<VirtioGpuSnapshot> {
1457 let snapshot_directory_tempdir = if let Some(dir) = &self.snapshot_scratch_directory {
1458 tempfile::tempdir_in(dir).with_context(|| {
1459 format!(
1460 "failed to create tempdir in {} for gpu rutabaga snapshot",
1461 dir.display()
1462 )
1463 })?
1464 } else {
1465 tempfile::tempdir().context("failed to create tempdir for gpu rutabaga snapshot")?
1466 };
1467 let snapshot_directory = snapshot_directory_tempdir.path();
1468
1469 Ok(VirtioGpuSnapshot {
1470 scanouts: self
1471 .scanouts
1472 .iter()
1473 .map(|(i, s)| (*i, s.snapshot()))
1474 .collect(),
1475 scanouts_updated: self.scanouts_updated.load(Ordering::SeqCst),
1476 cursor_scanout: self.cursor_scanout.snapshot(),
1477 rutabaga: {
1478 self.rutabaga
1479 .snapshot(snapshot_directory)
1480 .context("failed to snapshot rutabaga")?;
1481
1482 pack_directory_to_snapshot(snapshot_directory).with_context(|| {
1483 format!(
1484 "failed to pack rutabaga snapshot from {}",
1485 snapshot_directory.display()
1486 )
1487 })?
1488 },
1489 resources: self
1490 .resources
1491 .iter()
1492 .map(|(i, r)| (*i, r.snapshot()))
1493 .collect(),
1494 })
1495 }
1496
1497 pub fn restore(&mut self, snapshot: VirtioGpuSnapshot) -> anyhow::Result<()> {
1498 self.deferred_snapshot_load = Some(snapshot);
1499 Ok(())
1500 }
1501
1502 pub fn resume(&mut self, mem: &GuestMemory) -> anyhow::Result<()> {
1503 if let Some(snapshot) = self.deferred_snapshot_load.take() {
1504 assert!(self.scanouts.keys().eq(snapshot.scanouts.keys()));
1505 for (i, s) in snapshot.scanouts.into_iter() {
1506 self.scanouts
1507 .get_mut(&i)
1508 .unwrap()
1509 .restore(
1510 s,
1511 None,
1513 &self.display,
1514 )
1515 .context("failed to restore scanouts")?;
1516 }
1517 self.scanouts_updated
1518 .store(snapshot.scanouts_updated, Ordering::SeqCst);
1519
1520 let cursor_parent_surface_id = snapshot
1521 .cursor_scanout
1522 .parent_scanout_id
1523 .and_then(|i| self.scanouts.get(&i).unwrap().surface_id);
1524 self.cursor_scanout
1525 .restore(
1526 snapshot.cursor_scanout,
1527 cursor_parent_surface_id,
1528 &self.display,
1529 )
1530 .context("failed to restore cursor scanout")?;
1531
1532 let snapshot_directory_tempdir = if let Some(dir) = &self.snapshot_scratch_directory {
1533 tempfile::tempdir_in(dir).with_context(|| {
1534 format!(
1535 "failed to create tempdir in {} for gpu rutabaga snapshot",
1536 dir.display()
1537 )
1538 })?
1539 } else {
1540 tempfile::tempdir().context("failed to create tempdir for gpu rutabaga snapshot")?
1541 };
1542 let snapshot_directory = snapshot_directory_tempdir.path();
1543
1544 unpack_snapshot_to_directory(snapshot_directory, snapshot.rutabaga).with_context(
1545 || {
1546 format!(
1547 "failed to unpack rutabaga snapshot to {}",
1548 snapshot_directory.display()
1549 )
1550 },
1551 )?;
1552 self.rutabaga
1553 .restore(snapshot_directory)
1554 .context("failed to restore rutabaga")?;
1555
1556 for (id, s) in snapshot.resources.into_iter() {
1557 let backing_iovecs = s.backing_iovecs.clone();
1558 let shmem_offset = s.shmem_offset;
1559 self.resources.insert(id, VirtioGpuResource::restore(s));
1560 if let Some(backing_iovecs) = backing_iovecs {
1561 self.attach_backing(id, mem, backing_iovecs)
1562 .context("failed to restore resource backing")?;
1563 }
1564 if let Some(shmem_offset) = shmem_offset {
1565 self.resource_map_blob(id, shmem_offset)
1566 .context("failed to restore resource mapping")?;
1567 }
1568 }
1569 }
1570
1571 self.rutabaga.resume().context("failed to resume rutabaga")
1572 }
1573}