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