• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 // Copyright 2020 The Chromium OS Authors. All rights reserved.
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::num::NonZeroU32;
8 use std::rc::Rc;
9 use std::result::Result;
10 use std::sync::Arc;
11 
12 use crate::virtio::gpu::GpuDisplayParameters;
13 use crate::virtio::resource_bridge::{BufferInfo, PlaneInfo, ResourceInfo, ResourceResponse};
14 use base::{error, ExternalMapping, SafeDescriptor, Tube};
15 
16 use data_model::VolatileSlice;
17 
18 use gpu_display::*;
19 use rutabaga_gfx::{
20     ResourceCreate3D, ResourceCreateBlob, Rutabaga, RutabagaBuilder, RutabagaFence,
21     RutabagaFenceHandler, RutabagaIovec, Transfer3D,
22 };
23 
24 use libc::c_void;
25 
26 use resources::Alloc;
27 
28 use super::protocol::{
29     GpuResponse::{self, *},
30     GpuResponsePlaneInfo, VirtioGpuResult, VIRTIO_GPU_BLOB_FLAG_CREATE_GUEST_HANDLE,
31     VIRTIO_GPU_BLOB_MEM_HOST3D,
32 };
33 use super::udmabuf::UdmabufDriver;
34 use super::VirtioScanoutBlobData;
35 use sync::Mutex;
36 
37 use vm_memory::{GuestAddress, GuestMemory};
38 
39 use vm_control::{MemSlot, VmMemoryDestination, VmMemoryRequest, VmMemoryResponse, VmMemorySource};
40 
41 struct VirtioGpuResource {
42     resource_id: u32,
43     width: u32,
44     height: u32,
45     size: u64,
46     slot: Option<MemSlot>,
47     scanout_data: Option<VirtioScanoutBlobData>,
48     display_import: Option<u32>,
49 }
50 
51 impl VirtioGpuResource {
52     /// Creates a new VirtioGpuResource with the given metadata.  Width and height are used by the
53     /// display, while size is useful for hypervisor mapping.
new(resource_id: u32, width: u32, height: u32, size: u64) -> VirtioGpuResource54     pub fn new(resource_id: u32, width: u32, height: u32, size: u64) -> VirtioGpuResource {
55         VirtioGpuResource {
56             resource_id,
57             width,
58             height,
59             size,
60             slot: None,
61             scanout_data: None,
62             display_import: None,
63         }
64     }
65 }
66 
67 struct VirtioGpuScanout {
68     width: u32,
69     height: u32,
70     surface_id: Option<u32>,
71     resource_id: Option<NonZeroU32>,
72     scanout_type: SurfaceType,
73     // If this scanout is a primary scanout, the scanout id.
74     scanout_id: Option<u32>,
75     // If this scanout is a cursor scanout, the scanout that this is cursor is overlayed onto.
76     parent_surface_id: Option<u32>,
77 }
78 
79 impl VirtioGpuScanout {
new(width: u32, height: u32, scanout_id: u32) -> VirtioGpuScanout80     fn new(width: u32, height: u32, scanout_id: u32) -> VirtioGpuScanout {
81         VirtioGpuScanout {
82             width,
83             height,
84             scanout_type: SurfaceType::Scanout,
85             scanout_id: Some(scanout_id),
86             surface_id: None,
87             resource_id: None,
88             parent_surface_id: None,
89         }
90     }
91 
new_cursor() -> VirtioGpuScanout92     fn new_cursor() -> VirtioGpuScanout {
93         // Per virtio spec: "The mouse cursor image is a normal resource, except that it must be
94         // 64x64 in size."
95         VirtioGpuScanout {
96             width: 64,
97             height: 64,
98             scanout_type: SurfaceType::Cursor,
99             scanout_id: None,
100             surface_id: None,
101             resource_id: None,
102             parent_surface_id: None,
103         }
104     }
105 
create_surface( &mut self, display: &Rc<RefCell<GpuDisplay>>, new_parent_surface_id: Option<u32>, ) -> VirtioGpuResult106     fn create_surface(
107         &mut self,
108         display: &Rc<RefCell<GpuDisplay>>,
109         new_parent_surface_id: Option<u32>,
110     ) -> VirtioGpuResult {
111         let mut need_to_create = false;
112 
113         if self.surface_id.is_none() {
114             need_to_create = true;
115         }
116 
117         if self.parent_surface_id != new_parent_surface_id {
118             self.parent_surface_id = new_parent_surface_id;
119             need_to_create = true;
120         }
121 
122         if !need_to_create {
123             return Ok(OkNoData);
124         }
125 
126         self.release_surface(display);
127 
128         let mut display = display.borrow_mut();
129 
130         let surface_id = display.create_surface(
131             self.parent_surface_id,
132             self.width,
133             self.height,
134             self.scanout_type,
135         )?;
136 
137         if let Some(scanout_id) = self.scanout_id {
138             display.set_scanout_id(surface_id, scanout_id)?;
139         }
140 
141         self.surface_id = Some(surface_id);
142 
143         Ok(OkNoData)
144     }
145 
release_surface(&mut self, display: &Rc<RefCell<GpuDisplay>>)146     fn release_surface(&mut self, display: &Rc<RefCell<GpuDisplay>>) {
147         if let Some(surface_id) = self.surface_id {
148             display.borrow_mut().release_surface(surface_id);
149         }
150 
151         self.surface_id = None;
152     }
153 
set_position(&self, display: &Rc<RefCell<GpuDisplay>>, x: u32, y: u32) -> VirtioGpuResult154     fn set_position(&self, display: &Rc<RefCell<GpuDisplay>>, x: u32, y: u32) -> VirtioGpuResult {
155         if let Some(surface_id) = self.surface_id {
156             display.borrow_mut().set_position(surface_id, x, y)?;
157         }
158         Ok(OkNoData)
159     }
160 
commit(&self, display: &Rc<RefCell<GpuDisplay>>) -> VirtioGpuResult161     fn commit(&self, display: &Rc<RefCell<GpuDisplay>>) -> VirtioGpuResult {
162         if let Some(surface_id) = self.surface_id {
163             display.borrow_mut().commit(surface_id)?;
164         }
165         Ok(OkNoData)
166     }
167 
flush( &mut self, display: &Rc<RefCell<GpuDisplay>>, resource: &mut VirtioGpuResource, rutabaga: &mut Rutabaga, ) -> VirtioGpuResult168     fn flush(
169         &mut self,
170         display: &Rc<RefCell<GpuDisplay>>,
171         resource: &mut VirtioGpuResource,
172         rutabaga: &mut Rutabaga,
173     ) -> VirtioGpuResult {
174         let surface_id = match self.surface_id {
175             Some(id) => id,
176             _ => return Ok(OkNoData),
177         };
178 
179         if let Some(import_id) =
180             VirtioGpuScanout::import_resource_to_display(display, resource, rutabaga)
181         {
182             display.borrow_mut().flip_to(surface_id, import_id)?;
183             return Ok(OkNoData);
184         }
185 
186         // Import failed, fall back to a copy.
187         let mut display = display.borrow_mut();
188 
189         // Prevent overwriting a buffer that is currently being used by the compositor.
190         if display.next_buffer_in_use(surface_id) {
191             return Ok(OkNoData);
192         }
193 
194         let fb = display
195             .framebuffer_region(surface_id, 0, 0, self.width, self.height)
196             .ok_or(ErrUnspec)?;
197 
198         let mut transfer = Transfer3D::new_2d(0, 0, self.width, self.height);
199         transfer.stride = fb.stride();
200         rutabaga.transfer_read(
201             0,
202             resource.resource_id,
203             transfer,
204             Some(fb.as_volatile_slice()),
205         )?;
206 
207         display.flip(surface_id);
208         Ok(OkNoData)
209     }
210 
import_resource_to_display( display: &Rc<RefCell<GpuDisplay>>, resource: &mut VirtioGpuResource, rutabaga: &mut Rutabaga, ) -> Option<u32>211     fn import_resource_to_display(
212         display: &Rc<RefCell<GpuDisplay>>,
213         resource: &mut VirtioGpuResource,
214         rutabaga: &mut Rutabaga,
215     ) -> Option<u32> {
216         if let Some(import_id) = resource.display_import {
217             return Some(import_id);
218         }
219 
220         let dmabuf = rutabaga.export_blob(resource.resource_id).ok()?;
221         let query = rutabaga.query(resource.resource_id).ok()?;
222 
223         let (width, height, format, stride, offset) = match resource.scanout_data {
224             Some(data) => (
225                 data.width,
226                 data.height,
227                 data.drm_format.into(),
228                 data.strides[0],
229                 data.offsets[0],
230             ),
231             None => (
232                 resource.width,
233                 resource.height,
234                 query.drm_fourcc,
235                 query.strides[0],
236                 query.offsets[0],
237             ),
238         };
239 
240         let import_id = display
241             .borrow_mut()
242             .import_memory(
243                 &dmabuf.os_handle,
244                 offset,
245                 stride,
246                 query.modifier,
247                 width,
248                 height,
249                 format,
250             )
251             .ok()?;
252         resource.display_import = Some(import_id);
253         Some(import_id)
254     }
255 }
256 
257 /// Handles functionality related to displays, input events and hypervisor memory management.
258 pub struct VirtioGpu {
259     display: Rc<RefCell<GpuDisplay>>,
260     scanouts: Vec<VirtioGpuScanout>,
261     cursor_scanout: VirtioGpuScanout,
262     // Maps event devices to scanout number.
263     event_devices: Map<u32, u32>,
264     gpu_device_tube: Tube,
265     pci_bar: Alloc,
266     map_request: Arc<Mutex<Option<ExternalMapping>>>,
267     rutabaga: Rutabaga,
268     resources: Map<u32, VirtioGpuResource>,
269     external_blob: bool,
270     udmabuf_driver: Option<UdmabufDriver>,
271 }
272 
sglist_to_rutabaga_iovecs( vecs: &[(GuestAddress, usize)], mem: &GuestMemory, ) -> Result<Vec<RutabagaIovec>, ()>273 fn sglist_to_rutabaga_iovecs(
274     vecs: &[(GuestAddress, usize)],
275     mem: &GuestMemory,
276 ) -> Result<Vec<RutabagaIovec>, ()> {
277     if vecs
278         .iter()
279         .any(|&(addr, len)| mem.get_slice_at_addr(addr, len).is_err())
280     {
281         return Err(());
282     }
283 
284     let mut rutabaga_iovecs: Vec<RutabagaIovec> = Vec::new();
285     for &(addr, len) in vecs {
286         let slice = mem.get_slice_at_addr(addr, len).unwrap();
287         rutabaga_iovecs.push(RutabagaIovec {
288             base: slice.as_mut_ptr() as *mut c_void,
289             len,
290         });
291     }
292     Ok(rutabaga_iovecs)
293 }
294 
295 impl VirtioGpu {
296     /// Creates a new instance of the VirtioGpu state tracker.
new( display: GpuDisplay, display_params: Vec<GpuDisplayParameters>, rutabaga_builder: RutabagaBuilder, event_devices: Vec<EventDevice>, gpu_device_tube: Tube, pci_bar: Alloc, map_request: Arc<Mutex<Option<ExternalMapping>>>, external_blob: bool, udmabuf: bool, fence_handler: RutabagaFenceHandler, render_server_fd: Option<SafeDescriptor>, ) -> Option<VirtioGpu>297     pub fn new(
298         display: GpuDisplay,
299         display_params: Vec<GpuDisplayParameters>,
300         rutabaga_builder: RutabagaBuilder,
301         event_devices: Vec<EventDevice>,
302         gpu_device_tube: Tube,
303         pci_bar: Alloc,
304         map_request: Arc<Mutex<Option<ExternalMapping>>>,
305         external_blob: bool,
306         udmabuf: bool,
307         fence_handler: RutabagaFenceHandler,
308         render_server_fd: Option<SafeDescriptor>,
309     ) -> Option<VirtioGpu> {
310         let rutabaga = rutabaga_builder
311             .build(fence_handler, render_server_fd)
312             .map_err(|e| error!("failed to build rutabaga {}", e))
313             .ok()?;
314 
315         let mut udmabuf_driver = None;
316         if udmabuf {
317             udmabuf_driver = Some(
318                 UdmabufDriver::new()
319                     .map_err(|e| error!("failed to initialize udmabuf: {}", e))
320                     .ok()?,
321             );
322         }
323 
324         let scanouts = display_params
325             .iter()
326             .enumerate()
327             .map(|(display_index, &display_param)| {
328                 VirtioGpuScanout::new(
329                     display_param.width,
330                     display_param.height,
331                     display_index as u32,
332                 )
333             })
334             .collect::<Vec<_>>();
335         let cursor_scanout = VirtioGpuScanout::new_cursor();
336 
337         let mut virtio_gpu = VirtioGpu {
338             display: Rc::new(RefCell::new(display)),
339             scanouts,
340             cursor_scanout,
341             event_devices: Default::default(),
342             gpu_device_tube,
343             pci_bar,
344             map_request,
345             rutabaga,
346             resources: Default::default(),
347             external_blob,
348             udmabuf_driver,
349         };
350 
351         for event_device in event_devices {
352             virtio_gpu
353                 .import_event_device(event_device, 0)
354                 .map_err(|e| error!("failed to import event device {}", e))
355                 .ok()?;
356         }
357 
358         Some(virtio_gpu)
359     }
360 
361     /// Imports the event device
import_event_device( &mut self, event_device: EventDevice, scanout_id: u32, ) -> VirtioGpuResult362     pub fn import_event_device(
363         &mut self,
364         event_device: EventDevice,
365         scanout_id: u32,
366     ) -> VirtioGpuResult {
367         let mut display = self.display.borrow_mut();
368         let event_device_id = display.import_event_device(event_device)?;
369         self.event_devices.insert(event_device_id, scanout_id);
370         Ok(OkNoData)
371     }
372 
373     /// Gets a reference to the display passed into `new`.
display(&mut self) -> &Rc<RefCell<GpuDisplay>>374     pub fn display(&mut self) -> &Rc<RefCell<GpuDisplay>> {
375         &self.display
376     }
377 
378     /// Gets the list of supported display resolutions as a slice of `(width, height)` tuples.
display_info(&self) -> Vec<(u32, u32)>379     pub fn display_info(&self) -> Vec<(u32, u32)> {
380         self.scanouts
381             .iter()
382             .map(|scanout| (scanout.width, scanout.height))
383             .collect::<Vec<_>>()
384     }
385 
386     /// Processes the internal `display` events and returns `true` if any display was closed.
process_display(&mut self) -> bool387     pub fn process_display(&mut self) -> bool {
388         let mut display = self.display.borrow_mut();
389         let result = display.dispatch_events();
390         match result {
391             Ok(_) => (),
392             Err(e) => error!("failed to dispatch events: {}", e),
393         }
394 
395         for scanout in &self.scanouts {
396             let close_requested = scanout
397                 .surface_id
398                 .map(|surface_id| display.close_requested(surface_id))
399                 .unwrap_or(false);
400 
401             if close_requested {
402                 return true;
403             }
404         }
405 
406         false
407     }
408 
409     /// 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>, ) -> VirtioGpuResult410     pub fn set_scanout(
411         &mut self,
412         scanout_id: u32,
413         resource_id: u32,
414         scanout_data: Option<VirtioScanoutBlobData>,
415     ) -> VirtioGpuResult {
416         self.update_scanout_resource(SurfaceType::Scanout, scanout_id, scanout_data, resource_id)
417     }
418 
419     /// If the resource is the scanout resource, flush it to the display.
flush_resource(&mut self, resource_id: u32) -> VirtioGpuResult420     pub fn flush_resource(&mut self, resource_id: u32) -> VirtioGpuResult {
421         if resource_id == 0 {
422             return Ok(OkNoData);
423         }
424 
425         let resource = self
426             .resources
427             .get_mut(&resource_id)
428             .ok_or(ErrInvalidResourceId)?;
429 
430         // `resource_id` has already been verified to be non-zero
431         let resource_id = match NonZeroU32::new(resource_id) {
432             Some(id) => Some(id),
433             None => return Ok(OkNoData),
434         };
435 
436         for scanout in &mut self.scanouts {
437             if scanout.resource_id == resource_id {
438                 scanout.flush(&self.display, resource, &mut self.rutabaga)?;
439             }
440         }
441         if self.cursor_scanout.resource_id == resource_id {
442             self.cursor_scanout
443                 .flush(&self.display, resource, &mut self.rutabaga)?;
444         }
445 
446         Ok(OkNoData)
447     }
448 
449     /// Updates the cursor's memory to the given resource_id, and sets its position to the given
450     /// coordinates.
update_cursor( &mut self, resource_id: u32, scanout_id: u32, x: u32, y: u32, ) -> VirtioGpuResult451     pub fn update_cursor(
452         &mut self,
453         resource_id: u32,
454         scanout_id: u32,
455         x: u32,
456         y: u32,
457     ) -> VirtioGpuResult {
458         self.update_scanout_resource(SurfaceType::Cursor, scanout_id, None, resource_id)?;
459 
460         self.cursor_scanout.set_position(&self.display, x, y)?;
461 
462         self.flush_resource(resource_id)
463     }
464 
465     /// Moves the cursor's position to the given coordinates.
move_cursor(&mut self, _scanout_id: u32, x: u32, y: u32) -> VirtioGpuResult466     pub fn move_cursor(&mut self, _scanout_id: u32, x: u32, y: u32) -> VirtioGpuResult {
467         self.cursor_scanout.set_position(&self.display, x, y)?;
468         self.cursor_scanout.commit(&self.display)?;
469         Ok(OkNoData)
470     }
471 
472     /// Returns a uuid for the resource.
resource_assign_uuid(&self, resource_id: u32) -> VirtioGpuResult473     pub fn resource_assign_uuid(&self, resource_id: u32) -> VirtioGpuResult {
474         if !self.resources.contains_key(&resource_id) {
475             return Err(ErrInvalidResourceId);
476         }
477 
478         // TODO(stevensd): use real uuids once the virtio wayland protocol is updated to
479         // handle more than 32 bits. For now, the virtwl driver knows that the uuid is
480         // actually just the resource id.
481         let mut uuid: [u8; 16] = [0; 16];
482         for (idx, byte) in resource_id.to_be_bytes().iter().enumerate() {
483             uuid[12 + idx] = *byte;
484         }
485         Ok(OkResourceUuid { uuid })
486     }
487 
488     /// If supported, export the resource with the given `resource_id` to a file.
export_resource(&mut self, resource_id: u32) -> ResourceResponse489     pub fn export_resource(&mut self, resource_id: u32) -> ResourceResponse {
490         let file = match self.rutabaga.export_blob(resource_id) {
491             Ok(handle) => handle.os_handle.into(),
492             Err(_) => return ResourceResponse::Invalid,
493         };
494 
495         let q = match self.rutabaga.query(resource_id) {
496             Ok(query) => query,
497             Err(_) => return ResourceResponse::Invalid,
498         };
499 
500         ResourceResponse::Resource(ResourceInfo::Buffer(BufferInfo {
501             file,
502             planes: [
503                 PlaneInfo {
504                     offset: q.offsets[0],
505                     stride: q.strides[0],
506                 },
507                 PlaneInfo {
508                     offset: q.offsets[1],
509                     stride: q.strides[1],
510                 },
511                 PlaneInfo {
512                     offset: q.offsets[2],
513                     stride: q.strides[2],
514                 },
515                 PlaneInfo {
516                     offset: q.offsets[3],
517                     stride: q.strides[3],
518                 },
519             ],
520             modifier: q.modifier,
521         }))
522     }
523 
524     /// If supported, export the fence with the given `fence_id` to a file.
export_fence(&self, fence_id: u32) -> ResourceResponse525     pub fn export_fence(&self, fence_id: u32) -> ResourceResponse {
526         match self.rutabaga.export_fence(fence_id) {
527             Ok(handle) => ResourceResponse::Resource(ResourceInfo::Fence {
528                 file: handle.os_handle.into(),
529             }),
530             Err(_) => ResourceResponse::Invalid,
531         }
532     }
533 
534     /// Gets rutabaga's capset information associated with `index`.
get_capset_info(&self, index: u32) -> VirtioGpuResult535     pub fn get_capset_info(&self, index: u32) -> VirtioGpuResult {
536         let (capset_id, version, size) = self.rutabaga.get_capset_info(index)?;
537         Ok(OkCapsetInfo {
538             capset_id,
539             version,
540             size,
541         })
542     }
543 
544     /// Gets a capset from rutabaga.
get_capset(&self, capset_id: u32, version: u32) -> VirtioGpuResult545     pub fn get_capset(&self, capset_id: u32, version: u32) -> VirtioGpuResult {
546         let capset = self.rutabaga.get_capset(capset_id, version)?;
547         Ok(OkCapset(capset))
548     }
549 
550     /// Forces rutabaga to use it's default context.
force_ctx_0(&self)551     pub fn force_ctx_0(&self) {
552         self.rutabaga.force_ctx_0()
553     }
554 
555     /// Creates a fence with the RutabagaFence that can be used to determine when the previous
556     /// command completed.
create_fence(&mut self, rutabaga_fence: RutabagaFence) -> VirtioGpuResult557     pub fn create_fence(&mut self, rutabaga_fence: RutabagaFence) -> VirtioGpuResult {
558         self.rutabaga.create_fence(rutabaga_fence)?;
559         Ok(OkNoData)
560     }
561 
562     /// Polls the Rutabaga backend.
poll(&self)563     pub fn poll(&self) {
564         self.rutabaga.poll();
565     }
566 
567     /// Gets a pollable eventfd that signals the device to wakeup and poll the
568     /// Rutabaga backend.
poll_descriptor(&self) -> Option<SafeDescriptor>569     pub fn poll_descriptor(&self) -> Option<SafeDescriptor> {
570         self.rutabaga.poll_descriptor()
571     }
572 
573     /// Creates a 3D resource with the given properties and resource_id.
resource_create_3d( &mut self, resource_id: u32, resource_create_3d: ResourceCreate3D, ) -> VirtioGpuResult574     pub fn resource_create_3d(
575         &mut self,
576         resource_id: u32,
577         resource_create_3d: ResourceCreate3D,
578     ) -> VirtioGpuResult {
579         self.rutabaga
580             .resource_create_3d(resource_id, resource_create_3d)?;
581 
582         let resource = VirtioGpuResource::new(
583             resource_id,
584             resource_create_3d.width,
585             resource_create_3d.height,
586             0,
587         );
588 
589         // Rely on rutabaga to check for duplicate resource ids.
590         self.resources.insert(resource_id, resource);
591         Ok(self.result_from_query(resource_id))
592     }
593 
594     /// Attaches backing memory to the given resource, represented by a `Vec` of `(address, size)`
595     /// tuples in the guest's physical address space. Converts to RutabageIovec from the memory
596     /// mapping.
attach_backing( &mut self, resource_id: u32, mem: &GuestMemory, vecs: Vec<(GuestAddress, usize)>, ) -> VirtioGpuResult597     pub fn attach_backing(
598         &mut self,
599         resource_id: u32,
600         mem: &GuestMemory,
601         vecs: Vec<(GuestAddress, usize)>,
602     ) -> VirtioGpuResult {
603         let rutabaga_iovecs = sglist_to_rutabaga_iovecs(&vecs[..], mem).map_err(|_| ErrUnspec)?;
604         self.rutabaga.attach_backing(resource_id, rutabaga_iovecs)?;
605         Ok(OkNoData)
606     }
607 
608     /// Detaches any previously attached iovecs from the resource.
detach_backing(&mut self, resource_id: u32) -> VirtioGpuResult609     pub fn detach_backing(&mut self, resource_id: u32) -> VirtioGpuResult {
610         self.rutabaga.detach_backing(resource_id)?;
611         Ok(OkNoData)
612     }
613 
614     /// Releases guest kernel reference on the resource.
unref_resource(&mut self, resource_id: u32) -> VirtioGpuResult615     pub fn unref_resource(&mut self, resource_id: u32) -> VirtioGpuResult {
616         self.resources
617             .remove(&resource_id)
618             .ok_or(ErrInvalidResourceId)?;
619 
620         self.rutabaga.unref_resource(resource_id)?;
621         Ok(OkNoData)
622     }
623 
624     /// 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, ) -> VirtioGpuResult625     pub fn transfer_write(
626         &mut self,
627         ctx_id: u32,
628         resource_id: u32,
629         transfer: Transfer3D,
630     ) -> VirtioGpuResult {
631         self.rutabaga
632             .transfer_write(ctx_id, resource_id, transfer)?;
633         Ok(OkNoData)
634     }
635 
636     /// Copies data from the host resource to:
637     ///    1) To the optional volatile slice
638     ///    2) To the host resource's attached iovecs
639     ///
640     /// Can also be used to invalidate caches.
transfer_read( &mut self, ctx_id: u32, resource_id: u32, transfer: Transfer3D, buf: Option<VolatileSlice>, ) -> VirtioGpuResult641     pub fn transfer_read(
642         &mut self,
643         ctx_id: u32,
644         resource_id: u32,
645         transfer: Transfer3D,
646         buf: Option<VolatileSlice>,
647     ) -> VirtioGpuResult {
648         self.rutabaga
649             .transfer_read(ctx_id, resource_id, transfer, buf)?;
650         Ok(OkNoData)
651     }
652 
653     /// 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, ) -> VirtioGpuResult654     pub fn resource_create_blob(
655         &mut self,
656         ctx_id: u32,
657         resource_id: u32,
658         resource_create_blob: ResourceCreateBlob,
659         vecs: Vec<(GuestAddress, usize)>,
660         mem: &GuestMemory,
661     ) -> VirtioGpuResult {
662         let mut rutabaga_handle = None;
663         let mut rutabaga_iovecs = None;
664 
665         if resource_create_blob.blob_flags & VIRTIO_GPU_BLOB_FLAG_CREATE_GUEST_HANDLE != 0 {
666             rutabaga_handle = match self.udmabuf_driver {
667                 Some(ref driver) => Some(driver.create_udmabuf(mem, &vecs[..])?),
668                 None => return Err(ErrUnspec),
669             }
670         } else if resource_create_blob.blob_mem != VIRTIO_GPU_BLOB_MEM_HOST3D {
671             rutabaga_iovecs =
672                 Some(sglist_to_rutabaga_iovecs(&vecs[..], mem).map_err(|_| ErrUnspec)?);
673         }
674 
675         self.rutabaga.resource_create_blob(
676             ctx_id,
677             resource_id,
678             resource_create_blob,
679             rutabaga_iovecs,
680             rutabaga_handle,
681         )?;
682 
683         let resource = VirtioGpuResource::new(resource_id, 0, 0, resource_create_blob.size);
684 
685         // Rely on rutabaga to check for duplicate resource ids.
686         self.resources.insert(resource_id, resource);
687         Ok(self.result_from_query(resource_id))
688     }
689 
690     /// Uses the hypervisor to map the rutabaga blob resource.
resource_map_blob(&mut self, resource_id: u32, offset: u64) -> VirtioGpuResult691     pub fn resource_map_blob(&mut self, resource_id: u32, offset: u64) -> VirtioGpuResult {
692         let resource = self
693             .resources
694             .get_mut(&resource_id)
695             .ok_or(ErrInvalidResourceId)?;
696 
697         let map_info = self.rutabaga.map_info(resource_id).map_err(|_| ErrUnspec)?;
698         let vulkan_info_opt = self.rutabaga.vulkan_info(resource_id).ok();
699 
700         let source = if let Ok(export) = self.rutabaga.export_blob(resource_id) {
701             match vulkan_info_opt {
702                 Some(vulkan_info) => VmMemorySource::Vulkan {
703                     descriptor: export.os_handle,
704                     handle_type: export.handle_type,
705                     memory_idx: vulkan_info.memory_idx,
706                     physical_device_idx: vulkan_info.physical_device_idx,
707                     size: resource.size,
708                 },
709                 None => VmMemorySource::Descriptor {
710                     descriptor: export.os_handle,
711                     offset: 0,
712                     size: resource.size,
713                 },
714             }
715         } else {
716             if self.external_blob {
717                 return Err(ErrUnspec);
718             }
719 
720             let mapping = self.rutabaga.map(resource_id)?;
721             // Scope for lock
722             {
723                 let mut map_req = self.map_request.lock();
724                 if map_req.is_some() {
725                     return Err(ErrUnspec);
726                 }
727                 *map_req = Some(mapping);
728             }
729             VmMemorySource::ExternalMapping {
730                 size: resource.size,
731             }
732         };
733 
734         let request = VmMemoryRequest::RegisterMemory {
735             source,
736             dest: VmMemoryDestination::ExistingAllocation {
737                 allocation: self.pci_bar,
738                 offset,
739             },
740             read_only: false,
741         };
742         self.gpu_device_tube.send(&request)?;
743         let response = self.gpu_device_tube.recv()?;
744 
745         match response {
746             VmMemoryResponse::RegisterMemory { pfn: _, slot } => {
747                 resource.slot = Some(slot);
748                 Ok(OkMapInfo { map_info })
749             }
750             VmMemoryResponse::Err(e) => Err(ErrBase(e)),
751             _ => Err(ErrUnspec),
752         }
753     }
754 
755     /// Uses the hypervisor to unmap the blob resource.
resource_unmap_blob(&mut self, resource_id: u32) -> VirtioGpuResult756     pub fn resource_unmap_blob(&mut self, resource_id: u32) -> VirtioGpuResult {
757         let resource = self
758             .resources
759             .get_mut(&resource_id)
760             .ok_or(ErrInvalidResourceId)?;
761 
762         let slot = resource.slot.ok_or(ErrUnspec)?;
763         let request = VmMemoryRequest::UnregisterMemory(slot);
764         self.gpu_device_tube.send(&request)?;
765         let response = self.gpu_device_tube.recv()?;
766 
767         match response {
768             VmMemoryResponse::Ok => {
769                 resource.slot = None;
770                 Ok(OkNoData)
771             }
772             VmMemoryResponse::Err(e) => Err(ErrBase(e)),
773             _ => Err(ErrUnspec),
774         }
775     }
776 
777     /// Creates a rutabaga context.
create_context(&mut self, ctx_id: u32, context_init: u32) -> VirtioGpuResult778     pub fn create_context(&mut self, ctx_id: u32, context_init: u32) -> VirtioGpuResult {
779         self.rutabaga.create_context(ctx_id, context_init)?;
780         Ok(OkNoData)
781     }
782 
783     /// Destroys a rutabaga context.
destroy_context(&mut self, ctx_id: u32) -> VirtioGpuResult784     pub fn destroy_context(&mut self, ctx_id: u32) -> VirtioGpuResult {
785         self.rutabaga.destroy_context(ctx_id)?;
786         Ok(OkNoData)
787     }
788 
789     /// Attaches a resource to a rutabaga context.
context_attach_resource(&mut self, ctx_id: u32, resource_id: u32) -> VirtioGpuResult790     pub fn context_attach_resource(&mut self, ctx_id: u32, resource_id: u32) -> VirtioGpuResult {
791         self.rutabaga.context_attach_resource(ctx_id, resource_id)?;
792         Ok(OkNoData)
793     }
794 
795     /// Detaches a resource from a rutabaga context.
context_detach_resource(&mut self, ctx_id: u32, resource_id: u32) -> VirtioGpuResult796     pub fn context_detach_resource(&mut self, ctx_id: u32, resource_id: u32) -> VirtioGpuResult {
797         self.rutabaga.context_detach_resource(ctx_id, resource_id)?;
798         Ok(OkNoData)
799     }
800 
801     /// Submits a command buffer to a rutabaga context.
submit_command(&mut self, ctx_id: u32, commands: &mut [u8]) -> VirtioGpuResult802     pub fn submit_command(&mut self, ctx_id: u32, commands: &mut [u8]) -> VirtioGpuResult {
803         self.rutabaga.submit_command(ctx_id, commands)?;
804         Ok(OkNoData)
805     }
806 
807     // Non-public function -- no doc comment needed!
result_from_query(&mut self, resource_id: u32) -> GpuResponse808     fn result_from_query(&mut self, resource_id: u32) -> GpuResponse {
809         match self.rutabaga.query(resource_id) {
810             Ok(query) => {
811                 let mut plane_info = Vec::with_capacity(4);
812                 for plane_index in 0..4 {
813                     plane_info.push(GpuResponsePlaneInfo {
814                         stride: query.strides[plane_index],
815                         offset: query.offsets[plane_index],
816                     });
817                 }
818                 let format_modifier = query.modifier;
819                 OkResourcePlaneInfo {
820                     format_modifier,
821                     plane_info,
822                 }
823             }
824             Err(_) => OkNoData,
825         }
826     }
827 
update_scanout_resource( &mut self, scanout_type: SurfaceType, scanout_id: u32, scanout_data: Option<VirtioScanoutBlobData>, resource_id: u32, ) -> VirtioGpuResult828     fn update_scanout_resource(
829         &mut self,
830         scanout_type: SurfaceType,
831         scanout_id: u32,
832         scanout_data: Option<VirtioScanoutBlobData>,
833         resource_id: u32,
834     ) -> VirtioGpuResult {
835         let mut scanout: &mut VirtioGpuScanout;
836         let mut scanout_parent_surface_id = None;
837 
838         match scanout_type {
839             SurfaceType::Cursor => {
840                 let parent_scanout_id = scanout_id;
841 
842                 scanout_parent_surface_id = self
843                     .scanouts
844                     .get(parent_scanout_id as usize)
845                     .ok_or(ErrInvalidScanoutId)
846                     .map(|parent_scanout| parent_scanout.surface_id)?;
847 
848                 scanout = &mut self.cursor_scanout;
849             }
850             SurfaceType::Scanout => {
851                 scanout = self
852                     .scanouts
853                     .get_mut(scanout_id as usize)
854                     .ok_or(ErrInvalidScanoutId)?;
855             }
856         };
857 
858         // Virtio spec: "The driver can use resource_id = 0 to disable a scanout."
859         if resource_id == 0 {
860             // Ignore any initial set_scanout(..., resource_id: 0) calls.
861             if scanout.resource_id.is_some() {
862                 scanout.release_surface(&self.display);
863             }
864 
865             scanout.resource_id = None;
866             return Ok(OkNoData);
867         }
868 
869         let resource = self
870             .resources
871             .get_mut(&resource_id)
872             .ok_or(ErrInvalidResourceId)?;
873 
874         // Ensure scanout has a display surface.
875         match scanout_type {
876             SurfaceType::Cursor => {
877                 if let Some(scanout_parent_surface_id) = scanout_parent_surface_id {
878                     scanout.create_surface(&self.display, Some(scanout_parent_surface_id))?;
879                 }
880             }
881             SurfaceType::Scanout => {
882                 scanout.create_surface(&self.display, None)?;
883             }
884         }
885 
886         resource.scanout_data = scanout_data;
887 
888         // `resource_id` has already been verified to be non-zero
889         let resource_id = match NonZeroU32::new(resource_id) {
890             Some(id) => id,
891             None => return Ok(OkNoData),
892         };
893         scanout.resource_id = Some(resource_id);
894 
895         Ok(OkNoData)
896     }
897 }
898