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