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