• 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::resource_bridge::{BufferInfo, PlaneInfo, ResourceInfo, ResourceResponse};
13 use base::{error, AsRawDescriptor, ExternalMapping, Tube};
14 
15 use data_model::VolatileSlice;
16 
17 use gpu_display::*;
18 use rutabaga_gfx::{
19     ResourceCreate3D, ResourceCreateBlob, Rutabaga, RutabagaBuilder, RutabagaFenceData,
20     RutabagaIovec, Transfer3D,
21 };
22 
23 use libc::c_void;
24 
25 use resources::Alloc;
26 
27 use super::protocol::{
28     GpuResponse::{self, *},
29     GpuResponsePlaneInfo, VirtioGpuResult, VIRTIO_GPU_BLOB_FLAG_CREATE_GUEST_HANDLE,
30     VIRTIO_GPU_BLOB_MEM_HOST3D,
31 };
32 use super::udmabuf::UdmabufDriver;
33 use super::VirtioScanoutBlobData;
34 use sync::Mutex;
35 
36 use vm_memory::{GuestAddress, GuestMemory};
37 
38 use vm_control::{MemSlot, VmMemoryRequest, VmMemoryResponse};
39 
40 struct VirtioGpuResource {
41     resource_id: u32,
42     width: u32,
43     height: u32,
44     size: u64,
45     slot: Option<MemSlot>,
46     scanout_data: Option<VirtioScanoutBlobData>,
47     display_import: Option<(Rc<RefCell<GpuDisplay>>, u32)>,
48 }
49 
50 impl VirtioGpuResource {
51     /// Creates a new VirtioGpuResource with the given metadata.  Width and height are used by the
52     /// display, while size is useful for hypervisor mapping.
new(resource_id: u32, width: u32, height: u32, size: u64) -> VirtioGpuResource53     pub fn new(resource_id: u32, width: u32, height: u32, size: u64) -> VirtioGpuResource {
54         VirtioGpuResource {
55             resource_id,
56             width,
57             height,
58             size,
59             slot: None,
60             scanout_data: None,
61             display_import: None,
62         }
63     }
64 
65     /// Returns the dimensions of the VirtioGpuResource.
dimensions(&self) -> (u32, u32)66     pub fn dimensions(&self) -> (u32, u32) {
67         (self.width, self.height)
68     }
69 }
70 
71 /// Handles functionality related to displays, input events and hypervisor memory management.
72 pub struct VirtioGpu {
73     display: Rc<RefCell<GpuDisplay>>,
74     display_width: u32,
75     display_height: u32,
76     scanout_resource_id: Option<NonZeroU32>,
77     scanout_surface_id: Option<u32>,
78     cursor_resource_id: Option<NonZeroU32>,
79     cursor_surface_id: Option<u32>,
80     // Maps event devices to scanout number.
81     event_devices: Map<u32, u32>,
82     gpu_device_tube: Tube,
83     pci_bar: Alloc,
84     map_request: Arc<Mutex<Option<ExternalMapping>>>,
85     rutabaga: Rutabaga,
86     resources: Map<u32, VirtioGpuResource>,
87     external_blob: bool,
88     udmabuf_driver: Option<UdmabufDriver>,
89 }
90 
sglist_to_rutabaga_iovecs( vecs: &[(GuestAddress, usize)], mem: &GuestMemory, ) -> Result<Vec<RutabagaIovec>, ()>91 fn sglist_to_rutabaga_iovecs(
92     vecs: &[(GuestAddress, usize)],
93     mem: &GuestMemory,
94 ) -> Result<Vec<RutabagaIovec>, ()> {
95     if vecs
96         .iter()
97         .any(|&(addr, len)| mem.get_slice_at_addr(addr, len).is_err())
98     {
99         return Err(());
100     }
101 
102     let mut rutabaga_iovecs: Vec<RutabagaIovec> = Vec::new();
103     for &(addr, len) in vecs {
104         let slice = mem.get_slice_at_addr(addr, len).unwrap();
105         rutabaga_iovecs.push(RutabagaIovec {
106             base: slice.as_mut_ptr() as *mut c_void,
107             len,
108         });
109     }
110     Ok(rutabaga_iovecs)
111 }
112 
113 impl VirtioGpu {
114     /// Creates a new instance of the VirtioGpu state tracker.
new( display: GpuDisplay, display_width: u32, display_height: u32, 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, ) -> Option<VirtioGpu>115     pub fn new(
116         display: GpuDisplay,
117         display_width: u32,
118         display_height: u32,
119         rutabaga_builder: RutabagaBuilder,
120         event_devices: Vec<EventDevice>,
121         gpu_device_tube: Tube,
122         pci_bar: Alloc,
123         map_request: Arc<Mutex<Option<ExternalMapping>>>,
124         external_blob: bool,
125         udmabuf: bool,
126     ) -> Option<VirtioGpu> {
127         let rutabaga = rutabaga_builder
128             .build()
129             .map_err(|e| error!("failed to build rutabaga {}", e))
130             .ok()?;
131 
132         let mut udmabuf_driver = None;
133         if udmabuf {
134             udmabuf_driver = Some(
135                 UdmabufDriver::new()
136                     .map_err(|e| error!("failed to initialize udmabuf: {}", e))
137                     .ok()?,
138             );
139         }
140 
141         let mut virtio_gpu = VirtioGpu {
142             display: Rc::new(RefCell::new(display)),
143             display_width,
144             display_height,
145             event_devices: Default::default(),
146             scanout_resource_id: None,
147             scanout_surface_id: None,
148             cursor_resource_id: None,
149             cursor_surface_id: None,
150             gpu_device_tube,
151             pci_bar,
152             map_request,
153             rutabaga,
154             resources: Default::default(),
155             external_blob,
156             udmabuf_driver,
157         };
158 
159         for event_device in event_devices {
160             virtio_gpu
161                 .import_event_device(event_device, 0)
162                 .map_err(|e| error!("failed to import event device {}", e))
163                 .ok()?;
164         }
165 
166         Some(virtio_gpu)
167     }
168 
169     /// Imports the event device
import_event_device( &mut self, event_device: EventDevice, scanout: u32, ) -> VirtioGpuResult170     pub fn import_event_device(
171         &mut self,
172         event_device: EventDevice,
173         scanout: u32,
174     ) -> VirtioGpuResult {
175         // TODO(zachr): support more than one scanout.
176         if scanout != 0 {
177             return Err(ErrScanout {
178                 num_scanouts: scanout,
179             });
180         }
181 
182         let mut display = self.display.borrow_mut();
183         let event_device_id = display.import_event_device(event_device)?;
184         if let Some(s) = self.scanout_surface_id {
185             display.attach_event_device(s, event_device_id)
186         }
187         self.event_devices.insert(event_device_id, scanout);
188         Ok(OkNoData)
189     }
190 
191     /// Gets a reference to the display passed into `new`.
display(&mut self) -> &Rc<RefCell<GpuDisplay>>192     pub fn display(&mut self) -> &Rc<RefCell<GpuDisplay>> {
193         &self.display
194     }
195 
196     /// Gets the list of supported display resolutions as a slice of `(width, height)` tuples.
display_info(&self) -> [(u32, u32); 1]197     pub fn display_info(&self) -> [(u32, u32); 1] {
198         [(self.display_width, self.display_height)]
199     }
200 
201     /// Processes the internal `display` events and returns `true` if the main display was closed.
process_display(&mut self) -> bool202     pub fn process_display(&mut self) -> bool {
203         let mut display = self.display.borrow_mut();
204         display.dispatch_events();
205         self.scanout_surface_id
206             .map(|s| display.close_requested(s))
207             .unwrap_or(false)
208     }
209 
210     /// 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>, ) -> VirtioGpuResult211     pub fn set_scanout(
212         &mut self,
213         _scanout_id: u32,
214         resource_id: u32,
215         scanout_data: Option<VirtioScanoutBlobData>,
216     ) -> VirtioGpuResult {
217         let mut display = self.display.borrow_mut();
218         /// b/186580833.
219         /// Remove the part of deleting surface when resource_id is 0.
220         /// This is a workaround to solve the issue of black display.
221         /// Observation is when Surfaceflinger falls back to client composition,
222         /// host receives set_scanout 0 0, and then set scanout 0 <some valid resid>.
223         /// The first 0 0 removes the surface, the second creates a new surface
224         /// with id++, which will be more than 0 and be ignorned in vnc or webrtc
225         let resource = self
226             .resources
227             .get_mut(&resource_id)
228             .ok_or(ErrInvalidResourceId)?;
229 
230         resource.scanout_data = scanout_data;
231         self.scanout_resource_id = NonZeroU32::new(resource_id);
232         if self.scanout_surface_id.is_none() {
233             let surface_id =
234                 display.create_surface(None, self.display_width, self.display_height)?;
235             self.scanout_surface_id = Some(surface_id);
236             for event_device_id in self.event_devices.keys() {
237                 display.attach_event_device(surface_id, *event_device_id);
238             }
239         }
240         Ok(OkNoData)
241     }
242 
243     /// If the resource is the scanout resource, flush it to the display.
flush_resource(&mut self, resource_id: u32) -> VirtioGpuResult244     pub fn flush_resource(&mut self, resource_id: u32) -> VirtioGpuResult {
245         if resource_id == 0 {
246             return Ok(OkNoData);
247         }
248 
249         if let (Some(scanout_resource_id), Some(scanout_surface_id)) =
250             (self.scanout_resource_id, self.scanout_surface_id)
251         {
252             if scanout_resource_id.get() == resource_id {
253                 self.flush_resource_to_surface(resource_id, scanout_surface_id)?;
254             }
255         }
256 
257         if let (Some(cursor_resource_id), Some(cursor_surface_id)) =
258             (self.cursor_resource_id, self.cursor_surface_id)
259         {
260             if cursor_resource_id.get() == resource_id {
261                 self.flush_resource_to_surface(resource_id, cursor_surface_id)?;
262             }
263         }
264 
265         Ok(OkNoData)
266     }
267 
268     /// Attempts to import the given resource into the display.  Only works with Wayland displays.
import_to_display(&mut self, resource_id: u32) -> Option<u32>269     pub fn import_to_display(&mut self, resource_id: u32) -> Option<u32> {
270         let resource = match self.resources.get_mut(&resource_id) {
271             Some(resource) => resource,
272             _ => return None,
273         };
274 
275         if let Some((self_display, import)) = &resource.display_import {
276             if Rc::ptr_eq(self_display, &self.display) {
277                 return Some(*import);
278             }
279         }
280 
281         let dmabuf = self.rutabaga.export_blob(resource.resource_id).ok()?;
282         let query = self.rutabaga.query(resource.resource_id).ok()?;
283 
284         let (width, height, format, stride, offset) = match resource.scanout_data {
285             Some(data) => (
286                 data.width,
287                 data.height,
288                 data.drm_format.into(),
289                 data.strides[0],
290                 data.offsets[0],
291             ),
292             None => (
293                 resource.width,
294                 resource.height,
295                 query.drm_fourcc,
296                 query.strides[0],
297                 query.offsets[0],
298             ),
299         };
300 
301         match self.display.borrow_mut().import_dmabuf(
302             dmabuf.os_handle.as_raw_descriptor(),
303             offset,
304             stride,
305             query.modifier,
306             width,
307             height,
308             format,
309         ) {
310             Ok(import_id) => {
311                 resource.display_import = Some((self.display.clone(), import_id));
312                 Some(import_id)
313             }
314             Err(e) => {
315                 error!("failed to import dmabuf for display: {}", e);
316                 None
317             }
318         }
319     }
320 
321     /// Attempts to import the given resource into the display, otherwise falls back to rutabaga
322     /// copies.
flush_resource_to_surface( &mut self, resource_id: u32, surface_id: u32, ) -> VirtioGpuResult323     pub fn flush_resource_to_surface(
324         &mut self,
325         resource_id: u32,
326         surface_id: u32,
327     ) -> VirtioGpuResult {
328         if let Some(import_id) = self.import_to_display(resource_id) {
329             self.display.borrow_mut().flip_to(surface_id, import_id);
330             return Ok(OkNoData);
331         }
332 
333         if !self.resources.contains_key(&resource_id) {
334             return Err(ErrInvalidResourceId);
335         }
336 
337         // Import failed, fall back to a copy.
338         let mut display = self.display.borrow_mut();
339         // Prevent overwriting a buffer that is currently being used by the compositor.
340         if display.next_buffer_in_use(surface_id) {
341             return Ok(OkNoData);
342         }
343 
344         let fb = display
345             .framebuffer_region(surface_id, 0, 0, self.display_width, self.display_height)
346             .ok_or(ErrUnspec)?;
347 
348         let mut transfer = Transfer3D::new_2d(0, 0, self.display_width, self.display_height);
349         transfer.stride = fb.stride();
350         self.rutabaga
351             .transfer_read(0, resource_id, transfer, Some(fb.as_volatile_slice()))?;
352         display.flip(surface_id);
353 
354         Ok(OkNoData)
355     }
356 
357     /// Updates the cursor's memory to the given resource_id, and sets its position to the given
358     /// coordinates.
update_cursor(&mut self, resource_id: u32, x: u32, y: u32) -> VirtioGpuResult359     pub fn update_cursor(&mut self, resource_id: u32, x: u32, y: u32) -> VirtioGpuResult {
360         if resource_id == 0 {
361             if let Some(surface_id) = self.cursor_surface_id.take() {
362                 self.display.borrow_mut().release_surface(surface_id);
363             }
364             self.cursor_resource_id = None;
365             return Ok(OkNoData);
366         }
367 
368         let (resource_width, resource_height) = self
369             .resources
370             .get_mut(&resource_id)
371             .ok_or(ErrInvalidResourceId)?
372             .dimensions();
373 
374         self.cursor_resource_id = NonZeroU32::new(resource_id);
375 
376         if self.cursor_surface_id.is_none() {
377             self.cursor_surface_id = Some(self.display.borrow_mut().create_surface(
378                 self.scanout_surface_id,
379                 resource_width,
380                 resource_height,
381             )?);
382         }
383 
384         let cursor_surface_id = self.cursor_surface_id.unwrap();
385         self.display
386             .borrow_mut()
387             .set_position(cursor_surface_id, x, y);
388 
389         // Gets the resource's pixels into the display by importing the buffer.
390         if let Some(import_id) = self.import_to_display(resource_id) {
391             self.display
392                 .borrow_mut()
393                 .flip_to(cursor_surface_id, import_id);
394             return Ok(OkNoData);
395         }
396 
397         // Importing failed, so try copying the pixels into the surface's slower shared memory
398         // framebuffer.
399         if let Some(fb) = self.display.borrow_mut().framebuffer(cursor_surface_id) {
400             let mut transfer = Transfer3D::new_2d(0, 0, resource_width, resource_height);
401             transfer.stride = fb.stride();
402             self.rutabaga
403                 .transfer_read(0, resource_id, transfer, Some(fb.as_volatile_slice()))?;
404         }
405         self.display.borrow_mut().flip(cursor_surface_id);
406         Ok(OkNoData)
407     }
408 
409     /// Moves the cursor's position to the given coordinates.
move_cursor(&mut self, x: u32, y: u32) -> VirtioGpuResult410     pub fn move_cursor(&mut self, x: u32, y: u32) -> VirtioGpuResult {
411         if let Some(cursor_surface_id) = self.cursor_surface_id {
412             if let Some(scanout_surface_id) = self.scanout_surface_id {
413                 let mut display = self.display.borrow_mut();
414                 display.set_position(cursor_surface_id, x, y);
415                 display.commit(scanout_surface_id);
416             }
417         }
418         Ok(OkNoData)
419     }
420 
421     /// Returns a uuid for the resource.
resource_assign_uuid(&self, resource_id: u32) -> VirtioGpuResult422     pub fn resource_assign_uuid(&self, resource_id: u32) -> VirtioGpuResult {
423         if !self.resources.contains_key(&resource_id) {
424             return Err(ErrInvalidResourceId);
425         }
426 
427         // TODO(stevensd): use real uuids once the virtio wayland protocol is updated to
428         // handle more than 32 bits. For now, the virtwl driver knows that the uuid is
429         // actually just the resource id.
430         let mut uuid: [u8; 16] = [0; 16];
431         for (idx, byte) in resource_id.to_be_bytes().iter().enumerate() {
432             uuid[12 + idx] = *byte;
433         }
434         Ok(OkResourceUuid { uuid })
435     }
436 
437     /// If supported, export the resource with the given `resource_id` to a file.
export_resource(&mut self, resource_id: u32) -> ResourceResponse438     pub fn export_resource(&mut self, resource_id: u32) -> ResourceResponse {
439         let file = match self.rutabaga.export_blob(resource_id) {
440             Ok(handle) => handle.os_handle.into(),
441             Err(_) => return ResourceResponse::Invalid,
442         };
443 
444         let q = match self.rutabaga.query(resource_id) {
445             Ok(query) => query,
446             Err(_) => return ResourceResponse::Invalid,
447         };
448 
449         ResourceResponse::Resource(ResourceInfo::Buffer(BufferInfo {
450             file,
451             planes: [
452                 PlaneInfo {
453                     offset: q.offsets[0],
454                     stride: q.strides[0],
455                 },
456                 PlaneInfo {
457                     offset: q.offsets[1],
458                     stride: q.strides[1],
459                 },
460                 PlaneInfo {
461                     offset: q.offsets[2],
462                     stride: q.strides[2],
463                 },
464                 PlaneInfo {
465                     offset: q.offsets[3],
466                     stride: q.strides[3],
467                 },
468             ],
469             modifier: q.modifier,
470         }))
471     }
472 
473     /// If supported, export the fence with the given `fence_id` to a file.
export_fence(&self, fence_id: u32) -> ResourceResponse474     pub fn export_fence(&self, fence_id: u32) -> ResourceResponse {
475         match self.rutabaga.export_fence(fence_id) {
476             Ok(handle) => ResourceResponse::Resource(ResourceInfo::Fence {
477                 file: handle.os_handle.into(),
478             }),
479             Err(_) => ResourceResponse::Invalid,
480         }
481     }
482 
483     /// Gets rutabaga's capset information associated with `index`.
get_capset_info(&self, index: u32) -> VirtioGpuResult484     pub fn get_capset_info(&self, index: u32) -> VirtioGpuResult {
485         let (capset_id, version, size) = self.rutabaga.get_capset_info(index)?;
486         Ok(OkCapsetInfo {
487             capset_id,
488             version,
489             size,
490         })
491     }
492 
493     /// Gets a capset from rutabaga.
get_capset(&self, capset_id: u32, version: u32) -> VirtioGpuResult494     pub fn get_capset(&self, capset_id: u32, version: u32) -> VirtioGpuResult {
495         let capset = self.rutabaga.get_capset(capset_id, version)?;
496         Ok(OkCapset(capset))
497     }
498 
499     /// Forces rutabaga to use it's default context.
force_ctx_0(&self)500     pub fn force_ctx_0(&self) {
501         self.rutabaga.force_ctx_0()
502     }
503 
504     /// Creates a fence with the RutabagaFenceData that can be used to determine when the previous
505     /// command completed.
create_fence(&mut self, rutabaga_fence_data: RutabagaFenceData) -> VirtioGpuResult506     pub fn create_fence(&mut self, rutabaga_fence_data: RutabagaFenceData) -> VirtioGpuResult {
507         self.rutabaga.create_fence(rutabaga_fence_data)?;
508         Ok(OkNoData)
509     }
510 
511     /// Returns an array of RutabagaFenceData, describing completed fences.
fence_poll(&mut self) -> Vec<RutabagaFenceData>512     pub fn fence_poll(&mut self) -> Vec<RutabagaFenceData> {
513         self.rutabaga.poll()
514     }
515 
516     /// Creates a 3D resource with the given properties and resource_id.
resource_create_3d( &mut self, resource_id: u32, resource_create_3d: ResourceCreate3D, ) -> VirtioGpuResult517     pub fn resource_create_3d(
518         &mut self,
519         resource_id: u32,
520         resource_create_3d: ResourceCreate3D,
521     ) -> VirtioGpuResult {
522         self.rutabaga
523             .resource_create_3d(resource_id, resource_create_3d)?;
524 
525         let resource = VirtioGpuResource::new(
526             resource_id,
527             resource_create_3d.width,
528             resource_create_3d.height,
529             0,
530         );
531 
532         // Rely on rutabaga to check for duplicate resource ids.
533         self.resources.insert(resource_id, resource);
534         Ok(self.result_from_query(resource_id))
535     }
536 
537     /// Attaches backing memory to the given resource, represented by a `Vec` of `(address, size)`
538     /// tuples in the guest's physical address space. Converts to RutabageIovec from the memory
539     /// mapping.
attach_backing( &mut self, resource_id: u32, mem: &GuestMemory, vecs: Vec<(GuestAddress, usize)>, ) -> VirtioGpuResult540     pub fn attach_backing(
541         &mut self,
542         resource_id: u32,
543         mem: &GuestMemory,
544         vecs: Vec<(GuestAddress, usize)>,
545     ) -> VirtioGpuResult {
546         let rutabaga_iovecs = sglist_to_rutabaga_iovecs(&vecs[..], mem).map_err(|_| ErrUnspec)?;
547         self.rutabaga.attach_backing(resource_id, rutabaga_iovecs)?;
548         Ok(OkNoData)
549     }
550 
551     /// Detaches any previously attached iovecs from the resource.
detach_backing(&mut self, resource_id: u32) -> VirtioGpuResult552     pub fn detach_backing(&mut self, resource_id: u32) -> VirtioGpuResult {
553         self.rutabaga.detach_backing(resource_id)?;
554         Ok(OkNoData)
555     }
556 
557     /// Releases guest kernel reference on the resource.
unref_resource(&mut self, resource_id: u32) -> VirtioGpuResult558     pub fn unref_resource(&mut self, resource_id: u32) -> VirtioGpuResult {
559         self.resources
560             .remove(&resource_id)
561             .ok_or(ErrInvalidResourceId)?;
562 
563         self.rutabaga.unref_resource(resource_id)?;
564         Ok(OkNoData)
565     }
566 
567     /// 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, ) -> VirtioGpuResult568     pub fn transfer_write(
569         &mut self,
570         ctx_id: u32,
571         resource_id: u32,
572         transfer: Transfer3D,
573     ) -> VirtioGpuResult {
574         self.rutabaga
575             .transfer_write(ctx_id, resource_id, transfer)?;
576         Ok(OkNoData)
577     }
578 
579     /// Copies data from the host resource to:
580     ///    1) To the optional volatile slice
581     ///    2) To the host resource's attached iovecs
582     ///
583     /// Can also be used to invalidate caches.
transfer_read( &mut self, ctx_id: u32, resource_id: u32, transfer: Transfer3D, buf: Option<VolatileSlice>, ) -> VirtioGpuResult584     pub fn transfer_read(
585         &mut self,
586         ctx_id: u32,
587         resource_id: u32,
588         transfer: Transfer3D,
589         buf: Option<VolatileSlice>,
590     ) -> VirtioGpuResult {
591         self.rutabaga
592             .transfer_read(ctx_id, resource_id, transfer, buf)?;
593         Ok(OkNoData)
594     }
595 
596     /// 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, ) -> VirtioGpuResult597     pub fn resource_create_blob(
598         &mut self,
599         ctx_id: u32,
600         resource_id: u32,
601         resource_create_blob: ResourceCreateBlob,
602         vecs: Vec<(GuestAddress, usize)>,
603         mem: &GuestMemory,
604     ) -> VirtioGpuResult {
605         let mut rutabaga_handle = None;
606         let mut rutabaga_iovecs = None;
607 
608         if resource_create_blob.blob_flags & VIRTIO_GPU_BLOB_FLAG_CREATE_GUEST_HANDLE != 0 {
609             rutabaga_handle = match self.udmabuf_driver {
610                 Some(ref driver) => Some(driver.create_udmabuf(mem, &vecs[..])?),
611                 None => return Err(ErrUnspec),
612             }
613         } else if resource_create_blob.blob_mem != VIRTIO_GPU_BLOB_MEM_HOST3D {
614             rutabaga_iovecs =
615                 Some(sglist_to_rutabaga_iovecs(&vecs[..], mem).map_err(|_| ErrUnspec)?);
616         }
617 
618         self.rutabaga.resource_create_blob(
619             ctx_id,
620             resource_id,
621             resource_create_blob,
622             rutabaga_iovecs,
623             rutabaga_handle,
624         )?;
625 
626         let resource = VirtioGpuResource::new(resource_id, 0, 0, resource_create_blob.size);
627 
628         // Rely on rutabaga to check for duplicate resource ids.
629         self.resources.insert(resource_id, resource);
630         Ok(self.result_from_query(resource_id))
631     }
632 
633     /// Uses the hypervisor to map the rutabaga blob resource.
resource_map_blob(&mut self, resource_id: u32, offset: u64) -> VirtioGpuResult634     pub fn resource_map_blob(&mut self, resource_id: u32, offset: u64) -> VirtioGpuResult {
635         let resource = self
636             .resources
637             .get_mut(&resource_id)
638             .ok_or(ErrInvalidResourceId)?;
639 
640         let map_info = self.rutabaga.map_info(resource_id).map_err(|_| ErrUnspec)?;
641         let vulkan_info_opt = self.rutabaga.vulkan_info(resource_id).ok();
642 
643         let export = self.rutabaga.export_blob(resource_id);
644 
645         let request = match export {
646             Ok(export) => match vulkan_info_opt {
647                 Some(vulkan_info) => VmMemoryRequest::RegisterVulkanMemoryAtPciBarOffset {
648                     alloc: self.pci_bar,
649                     descriptor: export.os_handle,
650                     handle_type: export.handle_type,
651                     memory_idx: vulkan_info.memory_idx,
652                     physical_device_idx: vulkan_info.physical_device_idx,
653                     offset,
654                     size: resource.size,
655                 },
656                 None => VmMemoryRequest::RegisterFdAtPciBarOffset(
657                     self.pci_bar,
658                     export.os_handle,
659                     resource.size as usize,
660                     offset,
661                 ),
662             },
663             Err(_) => {
664                 if self.external_blob {
665                     return Err(ErrUnspec);
666                 }
667 
668                 let mapping = self.rutabaga.map(resource_id)?;
669                 // Scope for lock
670                 {
671                     let mut map_req = self.map_request.lock();
672                     if map_req.is_some() {
673                         return Err(ErrUnspec);
674                     }
675                     *map_req = Some(mapping);
676                 }
677                 VmMemoryRequest::RegisterHostPointerAtPciBarOffset(self.pci_bar, offset)
678             }
679         };
680 
681         self.gpu_device_tube.send(&request)?;
682         let response = self.gpu_device_tube.recv()?;
683 
684         match response {
685             VmMemoryResponse::RegisterMemory { pfn: _, slot } => {
686                 resource.slot = Some(slot);
687                 Ok(OkMapInfo { map_info })
688             }
689             VmMemoryResponse::Err(e) => Err(ErrSys(e)),
690             _ => Err(ErrUnspec),
691         }
692     }
693 
694     /// Uses the hypervisor to unmap the blob resource.
resource_unmap_blob(&mut self, resource_id: u32) -> VirtioGpuResult695     pub fn resource_unmap_blob(&mut self, resource_id: u32) -> VirtioGpuResult {
696         let resource = self
697             .resources
698             .get_mut(&resource_id)
699             .ok_or(ErrInvalidResourceId)?;
700 
701         let slot = resource.slot.ok_or(ErrUnspec)?;
702         let request = VmMemoryRequest::UnregisterMemory(slot);
703         self.gpu_device_tube.send(&request)?;
704         let response = self.gpu_device_tube.recv()?;
705 
706         match response {
707             VmMemoryResponse::Ok => {
708                 resource.slot = None;
709                 Ok(OkNoData)
710             }
711             VmMemoryResponse::Err(e) => Err(ErrSys(e)),
712             _ => Err(ErrUnspec),
713         }
714     }
715 
716     /// Creates a rutabaga context.
create_context(&mut self, ctx_id: u32, context_init: u32) -> VirtioGpuResult717     pub fn create_context(&mut self, ctx_id: u32, context_init: u32) -> VirtioGpuResult {
718         self.rutabaga.create_context(ctx_id, context_init)?;
719         Ok(OkNoData)
720     }
721 
722     /// Destroys a rutabaga context.
destroy_context(&mut self, ctx_id: u32) -> VirtioGpuResult723     pub fn destroy_context(&mut self, ctx_id: u32) -> VirtioGpuResult {
724         self.rutabaga.destroy_context(ctx_id)?;
725         Ok(OkNoData)
726     }
727 
728     /// Attaches a resource to a rutabaga context.
context_attach_resource(&mut self, ctx_id: u32, resource_id: u32) -> VirtioGpuResult729     pub fn context_attach_resource(&mut self, ctx_id: u32, resource_id: u32) -> VirtioGpuResult {
730         self.rutabaga.context_attach_resource(ctx_id, resource_id)?;
731         Ok(OkNoData)
732     }
733 
734     /// Detaches a resource from a rutabaga context.
context_detach_resource(&mut self, ctx_id: u32, resource_id: u32) -> VirtioGpuResult735     pub fn context_detach_resource(&mut self, ctx_id: u32, resource_id: u32) -> VirtioGpuResult {
736         self.rutabaga.context_detach_resource(ctx_id, resource_id)?;
737         Ok(OkNoData)
738     }
739 
740     /// Submits a command buffer to a rutabaga context.
submit_command(&mut self, ctx_id: u32, commands: &mut [u8]) -> VirtioGpuResult741     pub fn submit_command(&mut self, ctx_id: u32, commands: &mut [u8]) -> VirtioGpuResult {
742         self.rutabaga.submit_command(ctx_id, commands)?;
743         Ok(OkNoData)
744     }
745 
746     // Non-public function -- no doc comment needed!
result_from_query(&mut self, resource_id: u32) -> GpuResponse747     fn result_from_query(&mut self, resource_id: u32) -> GpuResponse {
748         match self.rutabaga.query(resource_id) {
749             Ok(query) => {
750                 let mut plane_info = Vec::with_capacity(4);
751                 for plane_index in 0..4 {
752                     plane_info.push(GpuResponsePlaneInfo {
753                         stride: query.strides[plane_index],
754                         offset: query.offsets[plane_index],
755                     });
756                 }
757                 let format_modifier = query.modifier;
758                 OkResourcePlaneInfo {
759                     format_modifier,
760                     plane_info,
761                 }
762             }
763             Err(_) => OkNoData,
764         }
765     }
766 }
767