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