• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 // Copyright 2020 The ChromiumOS Authors
2 // Use of this source code is governed by a BSD-style license that can be
3 // found in the LICENSE file.
4 
5 use std::cell::RefCell;
6 use std::collections::BTreeMap as Map;
7 use std::collections::BTreeSet as Set;
8 use std::num::NonZeroU32;
9 use std::rc::Rc;
10 use std::result::Result;
11 use std::sync::atomic::AtomicBool;
12 use std::sync::atomic::Ordering;
13 use std::sync::Arc;
14 
15 use base::error;
16 use base::info;
17 use base::FromRawDescriptor;
18 use base::IntoRawDescriptor;
19 use base::Protection;
20 use base::SafeDescriptor;
21 use data_model::VolatileSlice;
22 use gpu_display::*;
23 use libc::c_void;
24 use rutabaga_gfx::ResourceCreate3D;
25 use rutabaga_gfx::ResourceCreateBlob;
26 use rutabaga_gfx::Rutabaga;
27 use rutabaga_gfx::RutabagaBuilder;
28 use rutabaga_gfx::RutabagaDescriptor;
29 #[cfg(windows)]
30 use rutabaga_gfx::RutabagaError;
31 use rutabaga_gfx::RutabagaFence;
32 use rutabaga_gfx::RutabagaFenceHandler;
33 use rutabaga_gfx::RutabagaFromRawDescriptor;
34 use rutabaga_gfx::RutabagaHandle;
35 use rutabaga_gfx::RutabagaIntoRawDescriptor;
36 use rutabaga_gfx::RutabagaIovec;
37 use rutabaga_gfx::Transfer3D;
38 use rutabaga_gfx::RUTABAGA_MEM_HANDLE_TYPE_DMABUF;
39 use rutabaga_gfx::RUTABAGA_MEM_HANDLE_TYPE_OPAQUE_FD;
40 use vm_control::gpu::DisplayParameters;
41 use vm_control::gpu::GpuControlCommand;
42 use vm_control::gpu::GpuControlResult;
43 use vm_control::VmMemorySource;
44 use vm_memory::udmabuf::UdmabufDriver;
45 use vm_memory::udmabuf::UdmabufDriverTrait;
46 use vm_memory::GuestAddress;
47 use vm_memory::GuestMemory;
48 
49 use super::protocol::GpuResponse;
50 use super::protocol::GpuResponse::*;
51 use super::protocol::GpuResponsePlaneInfo;
52 use super::protocol::VirtioGpuResult;
53 use super::protocol::VIRTIO_GPU_BLOB_FLAG_CREATE_GUEST_HANDLE;
54 use super::protocol::VIRTIO_GPU_BLOB_MEM_HOST3D;
55 use super::VirtioScanoutBlobData;
56 use crate::virtio::gpu::edid::DisplayInfo;
57 use crate::virtio::gpu::edid::EdidBytes;
58 use crate::virtio::gpu::GpuDisplayParameters;
59 use crate::virtio::gpu::VIRTIO_GPU_MAX_SCANOUTS;
60 use crate::virtio::resource_bridge::BufferInfo;
61 use crate::virtio::resource_bridge::PlaneInfo;
62 use crate::virtio::resource_bridge::ResourceInfo;
63 use crate::virtio::resource_bridge::ResourceResponse;
64 use crate::virtio::SharedMemoryMapper;
65 
to_rutabaga_descriptor(s: SafeDescriptor) -> RutabagaDescriptor66 fn to_rutabaga_descriptor(s: SafeDescriptor) -> RutabagaDescriptor {
67     // Safe because we own the SafeDescriptor at this point.
68     unsafe { RutabagaDescriptor::from_raw_descriptor(s.into_raw_descriptor()) }
69 }
70 
to_safe_descriptor(r: RutabagaDescriptor) -> SafeDescriptor71 fn to_safe_descriptor(r: RutabagaDescriptor) -> SafeDescriptor {
72     // Safe because we own the SafeDescriptor at this point.
73     unsafe { SafeDescriptor::from_raw_descriptor(r.into_raw_descriptor()) }
74 }
75 
76 struct VirtioGpuResource {
77     resource_id: u32,
78     width: u32,
79     height: u32,
80     size: u64,
81     shmem_offset: Option<u64>,
82     scanout_data: Option<VirtioScanoutBlobData>,
83     display_import: Option<u32>,
84     rutabaga_external_mapping: bool,
85 }
86 
87 impl VirtioGpuResource {
88     /// Creates a new VirtioGpuResource with the given metadata.  Width and height are used by the
89     /// display, while size is useful for hypervisor mapping.
new(resource_id: u32, width: u32, height: u32, size: u64) -> VirtioGpuResource90     pub fn new(resource_id: u32, width: u32, height: u32, size: u64) -> VirtioGpuResource {
91         VirtioGpuResource {
92             resource_id,
93             width,
94             height,
95             size,
96             shmem_offset: None,
97             scanout_data: None,
98             display_import: None,
99             rutabaga_external_mapping: false,
100         }
101     }
102 }
103 
104 struct VirtioGpuScanout {
105     width: u32,
106     height: u32,
107     surface_id: Option<u32>,
108     resource_id: Option<NonZeroU32>,
109     scanout_type: SurfaceType,
110     // If this scanout is a primary scanout, the scanout id.
111     scanout_id: Option<u32>,
112     // If this scanout is a primary scanout, the display properties.
113     display_params: Option<GpuDisplayParameters>,
114     // If this scanout is a cursor scanout, the scanout that this is cursor is overlayed onto.
115     parent_surface_id: Option<u32>,
116 }
117 
118 impl VirtioGpuScanout {
new_primary(scanout_id: u32, params: GpuDisplayParameters) -> VirtioGpuScanout119     fn new_primary(scanout_id: u32, params: GpuDisplayParameters) -> VirtioGpuScanout {
120         let (width, height) = params.get_virtual_display_size();
121         VirtioGpuScanout {
122             width,
123             height,
124             scanout_type: SurfaceType::Scanout,
125             scanout_id: Some(scanout_id),
126             display_params: Some(params),
127             surface_id: None,
128             resource_id: None,
129             parent_surface_id: None,
130         }
131     }
132 
new_cursor() -> VirtioGpuScanout133     fn new_cursor() -> VirtioGpuScanout {
134         // Per virtio spec: "The mouse cursor image is a normal resource, except that it must be
135         // 64x64 in size."
136         VirtioGpuScanout {
137             width: 64,
138             height: 64,
139             scanout_type: SurfaceType::Cursor,
140             scanout_id: None,
141             display_params: None,
142             surface_id: None,
143             resource_id: None,
144             parent_surface_id: None,
145         }
146     }
147 
create_surface( &mut self, display: &Rc<RefCell<GpuDisplay>>, new_parent_surface_id: Option<u32>, ) -> VirtioGpuResult148     fn create_surface(
149         &mut self,
150         display: &Rc<RefCell<GpuDisplay>>,
151         new_parent_surface_id: Option<u32>,
152     ) -> VirtioGpuResult {
153         let mut need_to_create = false;
154 
155         if self.surface_id.is_none() {
156             need_to_create = true;
157         }
158 
159         if self.parent_surface_id != new_parent_surface_id {
160             self.parent_surface_id = new_parent_surface_id;
161             need_to_create = true;
162         }
163 
164         if !need_to_create {
165             return Ok(OkNoData);
166         }
167 
168         self.release_surface(display);
169 
170         let mut display = display.borrow_mut();
171 
172         let surface_id = display.create_surface(
173             self.parent_surface_id,
174             self.width,
175             self.height,
176             self.scanout_type,
177         )?;
178 
179         if let Some(scanout_id) = self.scanout_id {
180             display.set_scanout_id(surface_id, scanout_id)?;
181         }
182 
183         self.surface_id = Some(surface_id);
184 
185         Ok(OkNoData)
186     }
187 
release_surface(&mut self, display: &Rc<RefCell<GpuDisplay>>)188     fn release_surface(&mut self, display: &Rc<RefCell<GpuDisplay>>) {
189         if let Some(surface_id) = self.surface_id {
190             display.borrow_mut().release_surface(surface_id);
191         }
192 
193         self.surface_id = None;
194     }
195 
set_position(&self, display: &Rc<RefCell<GpuDisplay>>, x: u32, y: u32) -> VirtioGpuResult196     fn set_position(&self, display: &Rc<RefCell<GpuDisplay>>, x: u32, y: u32) -> VirtioGpuResult {
197         if let Some(surface_id) = self.surface_id {
198             display.borrow_mut().set_position(surface_id, x, y)?;
199         }
200         Ok(OkNoData)
201     }
202 
commit(&self, display: &Rc<RefCell<GpuDisplay>>) -> VirtioGpuResult203     fn commit(&self, display: &Rc<RefCell<GpuDisplay>>) -> VirtioGpuResult {
204         if let Some(surface_id) = self.surface_id {
205             display.borrow_mut().commit(surface_id)?;
206         }
207         Ok(OkNoData)
208     }
209 
flush( &mut self, display: &Rc<RefCell<GpuDisplay>>, resource: &mut VirtioGpuResource, rutabaga: &mut Rutabaga, ) -> VirtioGpuResult210     fn flush(
211         &mut self,
212         display: &Rc<RefCell<GpuDisplay>>,
213         resource: &mut VirtioGpuResource,
214         rutabaga: &mut Rutabaga,
215     ) -> VirtioGpuResult {
216         let surface_id = match self.surface_id {
217             Some(id) => id,
218             _ => return Ok(OkNoData),
219         };
220 
221         if let Some(import_id) =
222             VirtioGpuScanout::import_resource_to_display(display, resource, rutabaga)
223         {
224             display.borrow_mut().flip_to(surface_id, import_id)?;
225             return Ok(OkNoData);
226         }
227 
228         // Import failed, fall back to a copy.
229         let mut display = display.borrow_mut();
230 
231         // Prevent overwriting a buffer that is currently being used by the compositor.
232         if display.next_buffer_in_use(surface_id) {
233             return Ok(OkNoData);
234         }
235 
236         let fb = display
237             .framebuffer_region(surface_id, 0, 0, self.width, self.height)
238             .ok_or(ErrUnspec)?;
239 
240         let mut transfer = Transfer3D::new_2d(0, 0, self.width, self.height);
241         transfer.stride = fb.stride();
242         rutabaga.transfer_read(
243             0,
244             resource.resource_id,
245             transfer,
246             Some(fb.as_volatile_slice()),
247         )?;
248 
249         display.flip(surface_id);
250         Ok(OkNoData)
251     }
252 
import_resource_to_display( display: &Rc<RefCell<GpuDisplay>>, resource: &mut VirtioGpuResource, rutabaga: &mut Rutabaga, ) -> Option<u32>253     fn import_resource_to_display(
254         display: &Rc<RefCell<GpuDisplay>>,
255         resource: &mut VirtioGpuResource,
256         rutabaga: &mut Rutabaga,
257     ) -> Option<u32> {
258         if let Some(import_id) = resource.display_import {
259             return Some(import_id);
260         }
261 
262         let dmabuf = to_safe_descriptor(rutabaga.export_blob(resource.resource_id).ok()?.os_handle);
263         let query = rutabaga.query(resource.resource_id).ok()?;
264 
265         let (width, height, format, stride, offset) = match resource.scanout_data {
266             Some(data) => (
267                 data.width,
268                 data.height,
269                 data.drm_format.into(),
270                 data.strides[0],
271                 data.offsets[0],
272             ),
273             None => (
274                 resource.width,
275                 resource.height,
276                 query.drm_fourcc,
277                 query.strides[0],
278                 query.offsets[0],
279             ),
280         };
281 
282         let import_id = display
283             .borrow_mut()
284             .import_memory(
285                 &dmabuf,
286                 offset,
287                 stride,
288                 query.modifier,
289                 width,
290                 height,
291                 format,
292             )
293             .ok()?;
294         resource.display_import = Some(import_id);
295         Some(import_id)
296     }
297 }
298 
299 /// Handles functionality related to displays, input events and hypervisor memory management.
300 pub struct VirtioGpu {
301     display: Rc<RefCell<GpuDisplay>>,
302     scanouts: Map<u32, VirtioGpuScanout>,
303     scanouts_updated: Arc<AtomicBool>,
304     cursor_scanout: VirtioGpuScanout,
305     // Maps event devices to scanout number.
306     event_devices: Map<u32, u32>,
307     mapper: Box<dyn SharedMemoryMapper>,
308     rutabaga: Rutabaga,
309     resources: Map<u32, VirtioGpuResource>,
310     external_blob: bool,
311     udmabuf_driver: Option<UdmabufDriver>,
312 }
313 
sglist_to_rutabaga_iovecs( vecs: &[(GuestAddress, usize)], mem: &GuestMemory, ) -> Result<Vec<RutabagaIovec>, ()>314 fn sglist_to_rutabaga_iovecs(
315     vecs: &[(GuestAddress, usize)],
316     mem: &GuestMemory,
317 ) -> Result<Vec<RutabagaIovec>, ()> {
318     if vecs
319         .iter()
320         .any(|&(addr, len)| mem.get_slice_at_addr(addr, len).is_err())
321     {
322         return Err(());
323     }
324 
325     let mut rutabaga_iovecs: Vec<RutabagaIovec> = Vec::new();
326     for &(addr, len) in vecs {
327         let slice = mem.get_slice_at_addr(addr, len).unwrap();
328         rutabaga_iovecs.push(RutabagaIovec {
329             base: slice.as_mut_ptr() as *mut c_void,
330             len,
331         });
332     }
333     Ok(rutabaga_iovecs)
334 }
335 
336 pub enum ProcessDisplayResult {
337     Success,
338     CloseRequested,
339     Error(GpuDisplayError),
340 }
341 
342 impl VirtioGpu {
343     /// Creates a new instance of the VirtioGpu state tracker.
new( display: GpuDisplay, display_params: Vec<GpuDisplayParameters>, display_event: Arc<AtomicBool>, rutabaga_builder: RutabagaBuilder, event_devices: Vec<EventDevice>, mapper: Box<dyn SharedMemoryMapper>, external_blob: bool, udmabuf: bool, fence_handler: RutabagaFenceHandler, rutabaga_server_descriptor: Option<SafeDescriptor>, ) -> Option<VirtioGpu>344     pub fn new(
345         display: GpuDisplay,
346         display_params: Vec<GpuDisplayParameters>,
347         display_event: Arc<AtomicBool>,
348         rutabaga_builder: RutabagaBuilder,
349         event_devices: Vec<EventDevice>,
350         mapper: Box<dyn SharedMemoryMapper>,
351         external_blob: bool,
352         udmabuf: bool,
353         fence_handler: RutabagaFenceHandler,
354         rutabaga_server_descriptor: Option<SafeDescriptor>,
355     ) -> Option<VirtioGpu> {
356         let server_descriptor = rutabaga_server_descriptor.map(to_rutabaga_descriptor);
357 
358         let rutabaga = rutabaga_builder
359             .build(fence_handler, server_descriptor)
360             .map_err(|e| error!("failed to build rutabaga {}", e))
361             .ok()?;
362 
363         let mut udmabuf_driver = None;
364         if udmabuf {
365             udmabuf_driver = Some(
366                 UdmabufDriver::new()
367                     .map_err(|e| error!("failed to initialize udmabuf: {}", e))
368                     .ok()?,
369             );
370         }
371 
372         let scanouts = display_params
373             .iter()
374             .enumerate()
375             .map(|(display_index, display_param)| {
376                 (
377                     display_index as u32,
378                     VirtioGpuScanout::new_primary(display_index as u32, display_param.clone()),
379                 )
380             })
381             .collect::<Map<_, _>>();
382         let cursor_scanout = VirtioGpuScanout::new_cursor();
383 
384         let mut virtio_gpu = VirtioGpu {
385             display: Rc::new(RefCell::new(display)),
386             scanouts,
387             scanouts_updated: display_event,
388             cursor_scanout,
389             event_devices: Default::default(),
390             mapper,
391             rutabaga,
392             resources: Default::default(),
393             external_blob,
394             udmabuf_driver,
395         };
396 
397         for event_device in event_devices {
398             virtio_gpu
399                 .import_event_device(event_device, 0)
400                 .map_err(|e| error!("failed to import event device {}", e))
401                 .ok()?;
402         }
403 
404         Some(virtio_gpu)
405     }
406 
407     /// Imports the event device
import_event_device( &mut self, event_device: EventDevice, scanout_id: u32, ) -> VirtioGpuResult408     pub fn import_event_device(
409         &mut self,
410         event_device: EventDevice,
411         scanout_id: u32,
412     ) -> VirtioGpuResult {
413         let mut display = self.display.borrow_mut();
414         let event_device_id = display.import_event_device(event_device)?;
415         self.event_devices.insert(event_device_id, scanout_id);
416         Ok(OkNoData)
417     }
418 
419     /// Gets a reference to the display passed into `new`.
display(&mut self) -> &Rc<RefCell<GpuDisplay>>420     pub fn display(&mut self) -> &Rc<RefCell<GpuDisplay>> {
421         &self.display
422     }
423 
424     /// Gets the list of supported display resolutions as a slice of `(width, height, enabled)` tuples.
display_info(&self) -> Vec<(u32, u32, bool)>425     pub fn display_info(&self) -> Vec<(u32, u32, bool)> {
426         (0..VIRTIO_GPU_MAX_SCANOUTS)
427             .map(|scanout_id| scanout_id as u32)
428             .map(|scanout_id| {
429                 self.scanouts
430                     .get(&scanout_id)
431                     .map_or((0, 0, false), |scanout| {
432                         (scanout.width, scanout.height, true)
433                     })
434             })
435             .collect::<Vec<_>>()
436     }
437 
438     // Connects new displays to the device.
add_displays(&mut self, displays: Vec<DisplayParameters>) -> GpuControlResult439     fn add_displays(&mut self, displays: Vec<DisplayParameters>) -> GpuControlResult {
440         if self.scanouts.len() + displays.len() > VIRTIO_GPU_MAX_SCANOUTS {
441             return GpuControlResult::TooManyDisplays(VIRTIO_GPU_MAX_SCANOUTS);
442         }
443 
444         let mut available_scanout_ids = (0..VIRTIO_GPU_MAX_SCANOUTS)
445             .map(|s| s as u32)
446             .collect::<Set<u32>>();
447 
448         self.scanouts.keys().for_each(|scanout_id| {
449             available_scanout_ids.remove(scanout_id);
450         });
451 
452         for display_params in displays.into_iter() {
453             let new_scanout_id = *available_scanout_ids.iter().next().unwrap();
454             available_scanout_ids.remove(&new_scanout_id);
455 
456             self.scanouts.insert(
457                 new_scanout_id,
458                 VirtioGpuScanout::new_primary(new_scanout_id, display_params),
459             );
460         }
461 
462         self.scanouts_updated.store(true, Ordering::Relaxed);
463 
464         GpuControlResult::DisplaysUpdated
465     }
466 
467     /// Returns the list of displays currently connected to the device.
list_displays(&self) -> GpuControlResult468     fn list_displays(&self) -> GpuControlResult {
469         GpuControlResult::DisplayList {
470             displays: self
471                 .scanouts
472                 .iter()
473                 .filter_map(|(scanout_id, scanout)| {
474                     scanout
475                         .display_params
476                         .as_ref()
477                         .cloned()
478                         .map(|display_params| (*scanout_id, display_params))
479                 })
480                 .collect(),
481         }
482     }
483 
484     /// Removes the specified displays from the device.
remove_displays(&mut self, display_ids: Vec<u32>) -> GpuControlResult485     fn remove_displays(&mut self, display_ids: Vec<u32>) -> GpuControlResult {
486         let display_ids_to_remove = Set::from_iter(display_ids.iter());
487         display_ids_to_remove
488             .into_iter()
489             .try_for_each(|display_id| {
490                 self.scanouts
491                     .get_mut(display_id)
492                     .ok_or(GpuControlResult::NoSuchDisplay {
493                         display_id: *display_id,
494                     })
495                     .map(|scanout| {
496                         scanout.release_surface(&self.display);
497                         scanout
498                     })?;
499 
500                 self.scanouts.remove(display_id);
501 
502                 Ok(())
503             })
504             .err()
505             .unwrap_or_else(|| {
506                 self.scanouts_updated.store(true, Ordering::Relaxed);
507                 GpuControlResult::DisplaysUpdated
508             })
509     }
510 
511     /// Performs the given command to interact with or modify the device.
process_gpu_control_command(&mut self, cmd: GpuControlCommand) -> GpuControlResult512     pub fn process_gpu_control_command(&mut self, cmd: GpuControlCommand) -> GpuControlResult {
513         match cmd {
514             GpuControlCommand::AddDisplays { displays } => self.add_displays(displays),
515             GpuControlCommand::ListDisplays => self.list_displays(),
516             GpuControlCommand::RemoveDisplays { display_ids } => self.remove_displays(display_ids),
517         }
518     }
519 
520     /// Processes the internal `display` events and returns `true` if any display was closed.
process_display(&mut self) -> ProcessDisplayResult521     pub fn process_display(&mut self) -> ProcessDisplayResult {
522         let mut display = self.display.borrow_mut();
523         let result = display.dispatch_events();
524         match result {
525             Ok(_) => (),
526             Err(e) => {
527                 error!("failed to dispatch events: {}", e);
528                 return ProcessDisplayResult::Error(e);
529             }
530         }
531 
532         for scanout in self.scanouts.values() {
533             let close_requested = scanout
534                 .surface_id
535                 .map(|surface_id| display.close_requested(surface_id))
536                 .unwrap_or(false);
537 
538             if close_requested {
539                 return ProcessDisplayResult::CloseRequested;
540             }
541         }
542 
543         ProcessDisplayResult::Success
544     }
545 
546     /// Sets the given resource id as the source of scanout to the display.
set_scanout( &mut self, scanout_id: u32, resource_id: u32, scanout_data: Option<VirtioScanoutBlobData>, ) -> VirtioGpuResult547     pub fn set_scanout(
548         &mut self,
549         scanout_id: u32,
550         resource_id: u32,
551         scanout_data: Option<VirtioScanoutBlobData>,
552     ) -> VirtioGpuResult {
553         self.update_scanout_resource(SurfaceType::Scanout, scanout_id, scanout_data, resource_id)
554     }
555 
556     /// If the resource is the scanout resource, flush it to the display.
flush_resource(&mut self, resource_id: u32) -> VirtioGpuResult557     pub fn flush_resource(&mut self, resource_id: u32) -> VirtioGpuResult {
558         if resource_id == 0 {
559             return Ok(OkNoData);
560         }
561 
562         #[cfg(windows)]
563         match self.rutabaga.resource_flush(resource_id) {
564             Ok(_) => return Ok(OkNoData),
565             Err(RutabagaError::Unsupported) => {}
566             Err(e) => return Err(ErrRutabaga(e)),
567         }
568 
569         let resource = self
570             .resources
571             .get_mut(&resource_id)
572             .ok_or(ErrInvalidResourceId)?;
573 
574         // `resource_id` has already been verified to be non-zero
575         let resource_id = match NonZeroU32::new(resource_id) {
576             Some(id) => Some(id),
577             None => return Ok(OkNoData),
578         };
579 
580         for scanout in self.scanouts.values_mut() {
581             if scanout.resource_id == resource_id {
582                 scanout.flush(&self.display, resource, &mut self.rutabaga)?;
583             }
584         }
585         if self.cursor_scanout.resource_id == resource_id {
586             self.cursor_scanout
587                 .flush(&self.display, resource, &mut self.rutabaga)?;
588         }
589 
590         Ok(OkNoData)
591     }
592 
593     /// Updates the cursor's memory to the given resource_id, and sets its position to the given
594     /// coordinates.
update_cursor( &mut self, resource_id: u32, scanout_id: u32, x: u32, y: u32, ) -> VirtioGpuResult595     pub fn update_cursor(
596         &mut self,
597         resource_id: u32,
598         scanout_id: u32,
599         x: u32,
600         y: u32,
601     ) -> VirtioGpuResult {
602         self.update_scanout_resource(SurfaceType::Cursor, scanout_id, None, resource_id)?;
603 
604         self.cursor_scanout.set_position(&self.display, x, y)?;
605 
606         self.flush_resource(resource_id)
607     }
608 
609     /// Moves the cursor's position to the given coordinates.
move_cursor(&mut self, _scanout_id: u32, x: u32, y: u32) -> VirtioGpuResult610     pub fn move_cursor(&mut self, _scanout_id: u32, x: u32, y: u32) -> VirtioGpuResult {
611         self.cursor_scanout.set_position(&self.display, x, y)?;
612         self.cursor_scanout.commit(&self.display)?;
613         Ok(OkNoData)
614     }
615 
616     /// Returns a uuid for the resource.
resource_assign_uuid(&self, resource_id: u32) -> VirtioGpuResult617     pub fn resource_assign_uuid(&self, resource_id: u32) -> VirtioGpuResult {
618         if !self.resources.contains_key(&resource_id) {
619             return Err(ErrInvalidResourceId);
620         }
621 
622         // TODO(stevensd): use real uuids once the virtio wayland protocol is updated to
623         // handle more than 32 bits. For now, the virtwl driver knows that the uuid is
624         // actually just the resource id.
625         let mut uuid: [u8; 16] = [0; 16];
626         for (idx, byte) in resource_id.to_be_bytes().iter().enumerate() {
627             uuid[12 + idx] = *byte;
628         }
629         Ok(OkResourceUuid { uuid })
630     }
631 
632     /// If supported, export the resource with the given `resource_id` to a file.
export_resource(&mut self, resource_id: u32) -> ResourceResponse633     pub fn export_resource(&mut self, resource_id: u32) -> ResourceResponse {
634         let handle = match self.rutabaga.export_blob(resource_id) {
635             Ok(handle) => to_safe_descriptor(handle.os_handle),
636             Err(_) => return ResourceResponse::Invalid,
637         };
638 
639         let q = match self.rutabaga.query(resource_id) {
640             Ok(query) => query,
641             Err(_) => return ResourceResponse::Invalid,
642         };
643 
644         ResourceResponse::Resource(ResourceInfo::Buffer(BufferInfo {
645             handle,
646             planes: [
647                 PlaneInfo {
648                     offset: q.offsets[0],
649                     stride: q.strides[0],
650                 },
651                 PlaneInfo {
652                     offset: q.offsets[1],
653                     stride: q.strides[1],
654                 },
655                 PlaneInfo {
656                     offset: q.offsets[2],
657                     stride: q.strides[2],
658                 },
659                 PlaneInfo {
660                     offset: q.offsets[3],
661                     stride: q.strides[3],
662                 },
663             ],
664             modifier: q.modifier,
665         }))
666     }
667 
668     /// If supported, export the fence with the given `fence_id` to a file.
export_fence(&self, fence_id: u32) -> ResourceResponse669     pub fn export_fence(&self, fence_id: u32) -> ResourceResponse {
670         match self.rutabaga.export_fence(fence_id) {
671             Ok(handle) => ResourceResponse::Resource(ResourceInfo::Fence {
672                 handle: to_safe_descriptor(handle.os_handle),
673             }),
674             Err(_) => ResourceResponse::Invalid,
675         }
676     }
677 
678     /// Gets rutabaga's capset information associated with `index`.
get_capset_info(&self, index: u32) -> VirtioGpuResult679     pub fn get_capset_info(&self, index: u32) -> VirtioGpuResult {
680         let (capset_id, version, size) = self.rutabaga.get_capset_info(index)?;
681         Ok(OkCapsetInfo {
682             capset_id,
683             version,
684             size,
685         })
686     }
687 
688     /// Gets a capset from rutabaga.
get_capset(&self, capset_id: u32, version: u32) -> VirtioGpuResult689     pub fn get_capset(&self, capset_id: u32, version: u32) -> VirtioGpuResult {
690         info!("virgl get capset");
691         let capset = self.rutabaga.get_capset(capset_id, version)?;
692         info!("virgl get capset done");
693         Ok(OkCapset(capset))
694     }
695 
696     /// Forces rutabaga to use it's default context.
force_ctx_0(&self)697     pub fn force_ctx_0(&self) {
698         self.rutabaga.force_ctx_0()
699     }
700 
701     /// Creates a fence with the RutabagaFence that can be used to determine when the previous
702     /// command completed.
create_fence(&mut self, rutabaga_fence: RutabagaFence) -> VirtioGpuResult703     pub fn create_fence(&mut self, rutabaga_fence: RutabagaFence) -> VirtioGpuResult {
704         self.rutabaga.create_fence(rutabaga_fence)?;
705         Ok(OkNoData)
706     }
707 
708     /// Polls the Rutabaga backend.
event_poll(&self)709     pub fn event_poll(&self) {
710         self.rutabaga.event_poll();
711     }
712 
713     /// Gets a pollable eventfd that signals the device to wakeup and poll the
714     /// Rutabaga backend.
poll_descriptor(&self) -> Option<SafeDescriptor>715     pub fn poll_descriptor(&self) -> Option<SafeDescriptor> {
716         self.rutabaga.poll_descriptor().map(to_safe_descriptor)
717     }
718 
719     /// Creates a 3D resource with the given properties and resource_id.
resource_create_3d( &mut self, resource_id: u32, resource_create_3d: ResourceCreate3D, ) -> VirtioGpuResult720     pub fn resource_create_3d(
721         &mut self,
722         resource_id: u32,
723         resource_create_3d: ResourceCreate3D,
724     ) -> VirtioGpuResult {
725         self.rutabaga
726             .resource_create_3d(resource_id, resource_create_3d)?;
727 
728         let resource = VirtioGpuResource::new(
729             resource_id,
730             resource_create_3d.width,
731             resource_create_3d.height,
732             0,
733         );
734 
735         // Rely on rutabaga to check for duplicate resource ids.
736         self.resources.insert(resource_id, resource);
737         Ok(self.result_from_query(resource_id))
738     }
739 
740     /// Attaches backing memory to the given resource, represented by a `Vec` of `(address, size)`
741     /// tuples in the guest's physical address space. Converts to RutabagaIovec from the memory
742     /// mapping.
attach_backing( &mut self, resource_id: u32, mem: &GuestMemory, vecs: Vec<(GuestAddress, usize)>, ) -> VirtioGpuResult743     pub fn attach_backing(
744         &mut self,
745         resource_id: u32,
746         mem: &GuestMemory,
747         vecs: Vec<(GuestAddress, usize)>,
748     ) -> VirtioGpuResult {
749         let rutabaga_iovecs = sglist_to_rutabaga_iovecs(&vecs[..], mem).map_err(|_| ErrUnspec)?;
750         self.rutabaga.attach_backing(resource_id, rutabaga_iovecs)?;
751         Ok(OkNoData)
752     }
753 
754     /// Detaches any previously attached iovecs from the resource.
detach_backing(&mut self, resource_id: u32) -> VirtioGpuResult755     pub fn detach_backing(&mut self, resource_id: u32) -> VirtioGpuResult {
756         self.rutabaga.detach_backing(resource_id)?;
757         Ok(OkNoData)
758     }
759 
760     /// Releases guest kernel reference on the resource.
unref_resource(&mut self, resource_id: u32) -> VirtioGpuResult761     pub fn unref_resource(&mut self, resource_id: u32) -> VirtioGpuResult {
762         let resource = self
763             .resources
764             .remove(&resource_id)
765             .ok_or(ErrInvalidResourceId)?;
766 
767         if resource.rutabaga_external_mapping {
768             self.rutabaga.unmap(resource_id)?;
769         }
770 
771         self.rutabaga.unref_resource(resource_id)?;
772         Ok(OkNoData)
773     }
774 
775     /// Copies data to host resource from the attached iovecs. Can also be used to flush caches.
transfer_write( &mut self, ctx_id: u32, resource_id: u32, transfer: Transfer3D, ) -> VirtioGpuResult776     pub fn transfer_write(
777         &mut self,
778         ctx_id: u32,
779         resource_id: u32,
780         transfer: Transfer3D,
781     ) -> VirtioGpuResult {
782         self.rutabaga
783             .transfer_write(ctx_id, resource_id, transfer)?;
784         Ok(OkNoData)
785     }
786 
787     /// Copies data from the host resource to:
788     ///    1) To the optional volatile slice
789     ///    2) To the host resource's attached iovecs
790     ///
791     /// Can also be used to invalidate caches.
transfer_read( &mut self, ctx_id: u32, resource_id: u32, transfer: Transfer3D, buf: Option<VolatileSlice>, ) -> VirtioGpuResult792     pub fn transfer_read(
793         &mut self,
794         ctx_id: u32,
795         resource_id: u32,
796         transfer: Transfer3D,
797         buf: Option<VolatileSlice>,
798     ) -> VirtioGpuResult {
799         self.rutabaga
800             .transfer_read(ctx_id, resource_id, transfer, buf)?;
801         Ok(OkNoData)
802     }
803 
804     /// Creates a blob resource using rutabaga.
resource_create_blob( &mut self, ctx_id: u32, resource_id: u32, resource_create_blob: ResourceCreateBlob, vecs: Vec<(GuestAddress, usize)>, mem: &GuestMemory, ) -> VirtioGpuResult805     pub fn resource_create_blob(
806         &mut self,
807         ctx_id: u32,
808         resource_id: u32,
809         resource_create_blob: ResourceCreateBlob,
810         vecs: Vec<(GuestAddress, usize)>,
811         mem: &GuestMemory,
812     ) -> VirtioGpuResult {
813         let mut descriptor = None;
814         let mut rutabaga_iovecs = None;
815 
816         if resource_create_blob.blob_flags & VIRTIO_GPU_BLOB_FLAG_CREATE_GUEST_HANDLE != 0 {
817             descriptor = match self.udmabuf_driver {
818                 Some(ref driver) => Some(driver.create_udmabuf(mem, &vecs[..])?),
819                 None => return Err(ErrUnspec),
820             }
821         } else if resource_create_blob.blob_mem != VIRTIO_GPU_BLOB_MEM_HOST3D {
822             rutabaga_iovecs =
823                 Some(sglist_to_rutabaga_iovecs(&vecs[..], mem).map_err(|_| ErrUnspec)?);
824         }
825 
826         self.rutabaga.resource_create_blob(
827             ctx_id,
828             resource_id,
829             resource_create_blob,
830             rutabaga_iovecs,
831             descriptor.map(|descriptor| RutabagaHandle {
832                 os_handle: to_rutabaga_descriptor(descriptor),
833                 handle_type: RUTABAGA_MEM_HANDLE_TYPE_DMABUF,
834             }),
835         )?;
836 
837         let resource = VirtioGpuResource::new(resource_id, 0, 0, resource_create_blob.size);
838 
839         // Rely on rutabaga to check for duplicate resource ids.
840         self.resources.insert(resource_id, resource);
841         Ok(self.result_from_query(resource_id))
842     }
843 
844     /// Uses the hypervisor to map the rutabaga blob resource.
845     ///
846     /// When sandboxing is disabled, external_blob is unset and opaque fds are mapped by
847     /// rutabaga as ExternalMapping.
848     /// When sandboxing is enabled, external_blob is set and opaque fds must be mapped in the
849     /// hypervisor process by Vulkano using metadata provided by Rutabaga::vulkan_info().
resource_map_blob(&mut self, resource_id: u32, offset: u64) -> VirtioGpuResult850     pub fn resource_map_blob(&mut self, resource_id: u32, offset: u64) -> VirtioGpuResult {
851         let resource = self
852             .resources
853             .get_mut(&resource_id)
854             .ok_or(ErrInvalidResourceId)?;
855 
856         let map_info = self.rutabaga.map_info(resource_id).map_err(|_| ErrUnspec)?;
857 
858         let mut source: Option<VmMemorySource> = None;
859         if let Ok(export) = self.rutabaga.export_blob(resource_id) {
860             if let Ok(vulkan_info) = self.rutabaga.vulkan_info(resource_id) {
861                 source = Some(VmMemorySource::Vulkan {
862                     descriptor: to_safe_descriptor(export.os_handle),
863                     handle_type: export.handle_type,
864                     memory_idx: vulkan_info.memory_idx,
865                     device_id: vulkan_info.device_id,
866                     size: resource.size,
867                 });
868             } else if export.handle_type != RUTABAGA_MEM_HANDLE_TYPE_OPAQUE_FD {
869                 source = Some(VmMemorySource::Descriptor {
870                     descriptor: to_safe_descriptor(export.os_handle),
871                     offset: 0,
872                     size: resource.size,
873                 });
874             }
875         }
876 
877         // fallback to ExternalMapping via rutabaga if sandboxing (hence external_blob) is disabled.
878         if source.is_none() {
879             if self.external_blob {
880                 return Err(ErrUnspec);
881             }
882 
883             let mapping = self.rutabaga.map(resource_id)?;
884             // resources mapped via rutabaga must also be marked for unmap via rutabaga.
885             resource.rutabaga_external_mapping = true;
886             source = Some(VmMemorySource::ExternalMapping {
887                 ptr: mapping.ptr,
888                 size: mapping.size,
889             });
890         };
891 
892         self.mapper
893             .add_mapping(source.unwrap(), offset, Protection::read_write())
894             .map_err(|_| ErrUnspec)?;
895 
896         resource.shmem_offset = Some(offset);
897         Ok(OkMapInfo { map_info })
898     }
899 
900     /// Uses the hypervisor to unmap the blob resource.
resource_unmap_blob(&mut self, resource_id: u32) -> VirtioGpuResult901     pub fn resource_unmap_blob(&mut self, resource_id: u32) -> VirtioGpuResult {
902         let resource = self
903             .resources
904             .get_mut(&resource_id)
905             .ok_or(ErrInvalidResourceId)?;
906 
907         let shmem_offset = resource.shmem_offset.ok_or(ErrUnspec)?;
908         self.mapper
909             .remove_mapping(shmem_offset)
910             .map_err(|_| ErrUnspec)?;
911         resource.shmem_offset = None;
912 
913         if resource.rutabaga_external_mapping {
914             self.rutabaga.unmap(resource_id)?;
915             resource.rutabaga_external_mapping = false;
916         }
917 
918         Ok(OkNoData)
919     }
920 
921     /// Gets the EDID for the specified scanout ID. If that scanout is not enabled, it would return
922     /// the EDID of a default display.
get_edid(&self, scanout_id: u32) -> VirtioGpuResult923     pub fn get_edid(&self, scanout_id: u32) -> VirtioGpuResult {
924         let display_info = match self.scanouts.get(&scanout_id) {
925             Some(scanout) => {
926                 // Primary scanouts should always have display params.
927                 let params = scanout.display_params.as_ref().unwrap();
928                 DisplayInfo::new(params)
929             }
930             None => DisplayInfo::new(&Default::default()),
931         };
932         EdidBytes::new(&display_info)
933     }
934 
935     /// Creates a rutabaga context.
create_context( &mut self, ctx_id: u32, context_init: u32, context_name: Option<&str>, ) -> VirtioGpuResult936     pub fn create_context(
937         &mut self,
938         ctx_id: u32,
939         context_init: u32,
940         context_name: Option<&str>,
941     ) -> VirtioGpuResult {
942         self.rutabaga
943             .create_context(ctx_id, context_init, context_name)?;
944         Ok(OkNoData)
945     }
946 
947     /// Destroys a rutabaga context.
destroy_context(&mut self, ctx_id: u32) -> VirtioGpuResult948     pub fn destroy_context(&mut self, ctx_id: u32) -> VirtioGpuResult {
949         self.rutabaga.destroy_context(ctx_id)?;
950         Ok(OkNoData)
951     }
952 
953     /// Attaches a resource to a rutabaga context.
context_attach_resource(&mut self, ctx_id: u32, resource_id: u32) -> VirtioGpuResult954     pub fn context_attach_resource(&mut self, ctx_id: u32, resource_id: u32) -> VirtioGpuResult {
955         self.rutabaga.context_attach_resource(ctx_id, resource_id)?;
956         Ok(OkNoData)
957     }
958 
959     /// Detaches a resource from a rutabaga context.
context_detach_resource(&mut self, ctx_id: u32, resource_id: u32) -> VirtioGpuResult960     pub fn context_detach_resource(&mut self, ctx_id: u32, resource_id: u32) -> VirtioGpuResult {
961         self.rutabaga.context_detach_resource(ctx_id, resource_id)?;
962         Ok(OkNoData)
963     }
964 
965     /// Submits a command buffer to a rutabaga context.
submit_command(&mut self, ctx_id: u32, commands: &mut [u8]) -> VirtioGpuResult966     pub fn submit_command(&mut self, ctx_id: u32, commands: &mut [u8]) -> VirtioGpuResult {
967         self.rutabaga.submit_command(ctx_id, commands)?;
968         Ok(OkNoData)
969     }
970 
971     // Non-public function -- no doc comment needed!
result_from_query(&mut self, resource_id: u32) -> GpuResponse972     fn result_from_query(&mut self, resource_id: u32) -> GpuResponse {
973         match self.rutabaga.query(resource_id) {
974             Ok(query) => {
975                 let mut plane_info = Vec::with_capacity(4);
976                 for plane_index in 0..4 {
977                     plane_info.push(GpuResponsePlaneInfo {
978                         stride: query.strides[plane_index],
979                         offset: query.offsets[plane_index],
980                     });
981                 }
982                 let format_modifier = query.modifier;
983                 OkResourcePlaneInfo {
984                     format_modifier,
985                     plane_info,
986                 }
987             }
988             Err(_) => OkNoData,
989         }
990     }
991 
update_scanout_resource( &mut self, scanout_type: SurfaceType, scanout_id: u32, scanout_data: Option<VirtioScanoutBlobData>, resource_id: u32, ) -> VirtioGpuResult992     fn update_scanout_resource(
993         &mut self,
994         scanout_type: SurfaceType,
995         scanout_id: u32,
996         scanout_data: Option<VirtioScanoutBlobData>,
997         resource_id: u32,
998     ) -> VirtioGpuResult {
999         let mut scanout: &mut VirtioGpuScanout;
1000         let mut scanout_parent_surface_id = None;
1001 
1002         match scanout_type {
1003             SurfaceType::Cursor => {
1004                 let parent_scanout_id = scanout_id;
1005 
1006                 scanout_parent_surface_id = self
1007                     .scanouts
1008                     .get(&parent_scanout_id)
1009                     .ok_or(ErrInvalidScanoutId)
1010                     .map(|parent_scanout| parent_scanout.surface_id)?;
1011 
1012                 scanout = &mut self.cursor_scanout;
1013             }
1014             SurfaceType::Scanout => {
1015                 scanout = self
1016                     .scanouts
1017                     .get_mut(&scanout_id)
1018                     .ok_or(ErrInvalidScanoutId)?;
1019             }
1020         };
1021 
1022         // Virtio spec: "The driver can use resource_id = 0 to disable a scanout."
1023         if resource_id == 0 {
1024             // Ignore any initial set_scanout(..., resource_id: 0) calls.
1025             if scanout.resource_id.is_some() {
1026                 scanout.release_surface(&self.display);
1027             }
1028 
1029             scanout.resource_id = None;
1030             return Ok(OkNoData);
1031         }
1032 
1033         let resource = self
1034             .resources
1035             .get_mut(&resource_id)
1036             .ok_or(ErrInvalidResourceId)?;
1037 
1038         // Ensure scanout has a display surface.
1039         match scanout_type {
1040             SurfaceType::Cursor => {
1041                 if let Some(scanout_parent_surface_id) = scanout_parent_surface_id {
1042                     scanout.create_surface(&self.display, Some(scanout_parent_surface_id))?;
1043                 }
1044             }
1045             SurfaceType::Scanout => {
1046                 scanout.create_surface(&self.display, None)?;
1047             }
1048         }
1049 
1050         resource.scanout_data = scanout_data;
1051 
1052         // `resource_id` has already been verified to be non-zero
1053         let resource_id = match NonZeroU32::new(resource_id) {
1054             Some(id) => id,
1055             None => return Ok(OkNoData),
1056         };
1057         scanout.resource_id = Some(resource_id);
1058 
1059         Ok(OkNoData)
1060     }
1061 }
1062