xref: /aosp_15_r20/external/crosvm/rutabaga_gfx/src/virgl_renderer.rs (revision bb4ee6a4ae7042d18b07a98463b9c8b875e44b39)
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 //! virgl_renderer: Handles 3D virtio-gpu hypercalls using virglrenderer.
6 //! External code found at <https://gitlab.freedesktop.org/virgl/virglrenderer/>.
7 
8 #![cfg(feature = "virgl_renderer")]
9 
10 use std::cmp::min;
11 use std::io::Error as SysError;
12 use std::io::IoSliceMut;
13 use std::mem::size_of;
14 use std::mem::transmute;
15 use std::mem::ManuallyDrop;
16 use std::os::raw::c_char;
17 use std::os::raw::c_int;
18 use std::os::raw::c_void;
19 use std::panic::catch_unwind;
20 use std::process::abort;
21 use std::ptr::null_mut;
22 use std::sync::atomic::AtomicBool;
23 use std::sync::atomic::Ordering;
24 use std::sync::Arc;
25 
26 use log::debug;
27 use log::error;
28 use log::warn;
29 
30 use crate::generated::virgl_debug_callback_bindings::*;
31 use crate::generated::virgl_renderer_bindings::*;
32 use crate::renderer_utils::*;
33 use crate::rutabaga_core::RutabagaComponent;
34 use crate::rutabaga_core::RutabagaContext;
35 use crate::rutabaga_core::RutabagaResource;
36 use crate::rutabaga_os::FromRawDescriptor;
37 use crate::rutabaga_os::IntoRawDescriptor;
38 use crate::rutabaga_os::OwnedDescriptor;
39 use crate::rutabaga_os::RawDescriptor;
40 use crate::rutabaga_utils::*;
41 
42 type Query = virgl_renderer_export_query;
43 
dup(rd: RawDescriptor) -> RutabagaResult<OwnedDescriptor>44 fn dup(rd: RawDescriptor) -> RutabagaResult<OwnedDescriptor> {
45     // SAFETY:
46     // Safe because the underlying raw descriptor is guaranteed valid by rd's existence.
47     //
48     // Note that we are cloning the underlying raw descriptor since we have no guarantee of
49     // its existence after this function returns.
50     let rd_as_safe_desc = ManuallyDrop::new(unsafe { OwnedDescriptor::from_raw_descriptor(rd) });
51 
52     // We have to clone rd because we have no guarantee ownership was transferred (rd is
53     // borrowed).
54     Ok(rd_as_safe_desc.try_clone()?)
55 }
56 
57 /// The virtio-gpu backend state tracker which supports accelerated rendering.
58 pub struct VirglRenderer {}
59 
60 struct VirglRendererContext {
61     ctx_id: u32,
62 }
63 
import_resource(resource: &mut RutabagaResource) -> RutabagaResult<()>64 fn import_resource(resource: &mut RutabagaResource) -> RutabagaResult<()> {
65     if (resource.component_mask & (1 << (RutabagaComponentType::VirglRenderer as u8))) != 0 {
66         return Ok(());
67     }
68 
69     if let Some(handle) = &resource.handle {
70         if handle.handle_type == RUTABAGA_MEM_HANDLE_TYPE_DMABUF {
71             let dmabuf_fd = handle.os_handle.try_clone()?.into_raw_descriptor();
72             // SAFETY:
73             // Safe because we are being passed a valid fd
74             unsafe {
75                 let dmabuf_size = libc::lseek64(dmabuf_fd, 0, libc::SEEK_END);
76                 libc::lseek64(dmabuf_fd, 0, libc::SEEK_SET);
77                 let args = virgl_renderer_resource_import_blob_args {
78                     res_handle: resource.resource_id,
79                     blob_mem: resource.blob_mem,
80                     fd_type: VIRGL_RENDERER_BLOB_FD_TYPE_DMABUF,
81                     fd: dmabuf_fd,
82                     size: dmabuf_size as u64,
83                 };
84                 let ret = virgl_renderer_resource_import_blob(&args);
85                 if ret != 0 {
86                     // import_blob can fail if we've previously imported this resource,
87                     // but in any case virglrenderer does not take ownership of the fd
88                     // in error paths
89                     //
90                     // Because of the re-import case we must still fall through to the
91                     // virgl_renderer_ctx_attach_resource() call.
92                     libc::close(dmabuf_fd);
93                     return Ok(());
94                 }
95                 resource.component_mask |= 1 << (RutabagaComponentType::VirglRenderer as u8);
96             }
97         }
98     }
99 
100     Ok(())
101 }
102 
103 impl RutabagaContext for VirglRendererContext {
submit_cmd( &mut self, commands: &mut [u8], fence_ids: &[u64], _shareable_fences: Vec<RutabagaHandle>, ) -> RutabagaResult<()>104     fn submit_cmd(
105         &mut self,
106         commands: &mut [u8],
107         fence_ids: &[u64],
108         _shareable_fences: Vec<RutabagaHandle>,
109     ) -> RutabagaResult<()> {
110         #[cfg(not(virgl_renderer_unstable))]
111         if !fence_ids.is_empty() {
112             return Err(RutabagaError::Unsupported);
113         }
114         if commands.len() % size_of::<u32>() != 0 {
115             return Err(RutabagaError::InvalidCommandSize(commands.len()));
116         }
117         let dword_count = (commands.len() / size_of::<u32>()) as i32;
118         #[cfg(not(virgl_renderer_unstable))]
119         // SAFETY:
120         // Safe because the context and buffer are valid and virglrenderer will have been
121         // initialized if there are Context instances.
122         let ret = unsafe {
123             virgl_renderer_submit_cmd(
124                 commands.as_mut_ptr() as *mut c_void,
125                 self.ctx_id as i32,
126                 dword_count,
127             )
128         };
129         #[cfg(virgl_renderer_unstable)]
130         // SAFETY:
131         // Safe because the context and buffers are valid and virglrenderer will have been
132         // initialized if there are Context instances.
133         let ret = unsafe {
134             virgl_renderer_submit_cmd2(
135                 commands.as_mut_ptr() as *mut c_void,
136                 self.ctx_id as i32,
137                 dword_count,
138                 fence_ids.as_ptr() as *mut u64,
139                 fence_ids.len() as u32,
140             )
141         };
142         ret_to_res(ret)
143     }
144 
attach(&mut self, resource: &mut RutabagaResource)145     fn attach(&mut self, resource: &mut RutabagaResource) {
146         match import_resource(resource) {
147             Ok(()) => (),
148             Err(e) => error!("importing resource failing with {}", e),
149         }
150 
151         // SAFETY:
152         // The context id and resource id must be valid because the respective instances ensure
153         // their lifetime.
154         unsafe {
155             virgl_renderer_ctx_attach_resource(self.ctx_id as i32, resource.resource_id as i32);
156         }
157     }
158 
detach(&mut self, resource: &RutabagaResource)159     fn detach(&mut self, resource: &RutabagaResource) {
160         // SAFETY:
161         // The context id and resource id must be valid because the respective instances ensure
162         // their lifetime.
163         unsafe {
164             virgl_renderer_ctx_detach_resource(self.ctx_id as i32, resource.resource_id as i32);
165         }
166     }
167 
component_type(&self) -> RutabagaComponentType168     fn component_type(&self) -> RutabagaComponentType {
169         RutabagaComponentType::VirglRenderer
170     }
171 
context_create_fence( &mut self, fence: RutabagaFence, ) -> RutabagaResult<Option<RutabagaHandle>>172     fn context_create_fence(
173         &mut self,
174         fence: RutabagaFence,
175     ) -> RutabagaResult<Option<RutabagaHandle>> {
176         // RutabagaFence::flags are not compatible with virglrenderer's fencing API and currently
177         // virglrenderer context's assume all fences on a single timeline are MERGEABLE, and enforce
178         // this assumption.
179         let flags: u32 = VIRGL_RENDERER_FENCE_FLAG_MERGEABLE;
180 
181         // TODO(b/315870313): Add safety comment
182         #[allow(clippy::undocumented_unsafe_blocks)]
183         let ret = unsafe {
184             virgl_renderer_context_create_fence(
185                 fence.ctx_id,
186                 flags,
187                 fence.ring_idx as u32,
188                 fence.fence_id,
189             )
190         };
191         ret_to_res(ret)?;
192         Ok(None)
193     }
194 }
195 
196 impl Drop for VirglRendererContext {
drop(&mut self)197     fn drop(&mut self) {
198         // SAFETY:
199         // The context is safe to destroy because nothing else can be referencing it.
200         unsafe {
201             virgl_renderer_context_destroy(self.ctx_id);
202         }
203     }
204 }
205 
debug_callback(fmt: *const ::std::os::raw::c_char, ap: stdio::va_list)206 extern "C" fn debug_callback(fmt: *const ::std::os::raw::c_char, ap: stdio::va_list) {
207     const BUF_LEN: usize = 256;
208     let mut v = [b' '; BUF_LEN];
209 
210     // TODO(b/315870313): Add safety comment
211     #[allow(clippy::undocumented_unsafe_blocks)]
212     let printed_len = unsafe {
213         let ptr = v.as_mut_ptr() as *mut ::std::os::raw::c_char;
214         #[cfg(any(
215             target_arch = "x86",
216             target_arch = "x86_64",
217             target_arch = "aarch64",
218             target_arch = "riscv64"
219         ))]
220         let size = BUF_LEN as ::std::os::raw::c_ulong;
221         #[cfg(target_arch = "arm")]
222         let size = BUF_LEN as ::std::os::raw::c_uint;
223 
224         stdio::vsnprintf(ptr, size, fmt, ap)
225     };
226 
227     if printed_len < 0 {
228         debug!(
229             "rutabaga_gfx::virgl_renderer::debug_callback: vsnprintf returned {}",
230             printed_len
231         );
232     } else {
233         // vsnprintf returns the number of chars that *would* have been printed
234         let len = min(printed_len as usize, BUF_LEN - 1);
235         debug!("{}", String::from_utf8_lossy(&v[..len]));
236     }
237 }
238 
write_context_fence(cookie: *mut c_void, ctx_id: u32, ring_idx: u32, fence_id: u64)239 extern "C" fn write_context_fence(cookie: *mut c_void, ctx_id: u32, ring_idx: u32, fence_id: u64) {
240     catch_unwind(|| {
241         assert!(!cookie.is_null());
242         // TODO(b/315870313): Add safety comment
243         #[allow(clippy::undocumented_unsafe_blocks)]
244         let cookie = unsafe { &*(cookie as *mut RutabagaCookie) };
245 
246         // Call fence completion callback
247         if let Some(handler) = &cookie.fence_handler {
248             handler.call(RutabagaFence {
249                 flags: RUTABAGA_FLAG_FENCE | RUTABAGA_FLAG_INFO_RING_IDX,
250                 fence_id,
251                 ctx_id,
252                 ring_idx: ring_idx as u8,
253             });
254         }
255     })
256     .unwrap_or_else(|_| abort())
257 }
258 
259 // TODO(b/315870313): Add safety comment
260 #[allow(clippy::undocumented_unsafe_blocks)]
write_fence(cookie: *mut c_void, fence: u32)261 unsafe extern "C" fn write_fence(cookie: *mut c_void, fence: u32) {
262     catch_unwind(|| {
263         assert!(!cookie.is_null());
264         let cookie = &*(cookie as *mut RutabagaCookie);
265 
266         // Call fence completion callback
267         if let Some(handler) = &cookie.fence_handler {
268             handler.call(RutabagaFence {
269                 flags: RUTABAGA_FLAG_FENCE,
270                 fence_id: fence as u64,
271                 ctx_id: 0,
272                 ring_idx: 0,
273             });
274         }
275     })
276     .unwrap_or_else(|_| abort())
277 }
278 
279 // TODO(b/315870313): Add safety comment
280 #[allow(clippy::undocumented_unsafe_blocks)]
get_server_fd(cookie: *mut c_void, version: u32) -> c_int281 unsafe extern "C" fn get_server_fd(cookie: *mut c_void, version: u32) -> c_int {
282     catch_unwind(|| {
283         assert!(!cookie.is_null());
284         let cookie = &mut *(cookie as *mut RutabagaCookie);
285 
286         if version != 0 {
287             return -1;
288         }
289 
290         // Transfer the fd ownership to virglrenderer.
291         cookie
292             .render_server_fd
293             .take()
294             .map(OwnedDescriptor::into_raw_descriptor)
295             .unwrap_or(-1)
296     })
297     .unwrap_or_else(|_| abort())
298 }
299 
300 const VIRGL_RENDERER_CALLBACKS: &virgl_renderer_callbacks = &virgl_renderer_callbacks {
301     version: 3,
302     write_fence: Some(write_fence),
303     create_gl_context: None,
304     destroy_gl_context: None,
305     make_current: None,
306     get_drm_fd: None,
307     write_context_fence: Some(write_context_fence),
308     get_server_fd: Some(get_server_fd),
309     get_egl_display: None,
310 };
311 
312 /// Retrieves metadata suitable for export about this resource. If "export_fd" is true,
313 /// performs an export of this resource so that it may be imported by other processes.
export_query(resource_id: u32) -> RutabagaResult<Query>314 fn export_query(resource_id: u32) -> RutabagaResult<Query> {
315     let mut query: Query = Default::default();
316     query.hdr.stype = VIRGL_RENDERER_STRUCTURE_TYPE_EXPORT_QUERY;
317     query.hdr.stype_version = 0;
318     query.hdr.size = size_of::<Query>() as u32;
319     query.in_resource_id = resource_id;
320     query.in_export_fds = 0;
321 
322     let ret =
323         // SAFETY:
324         // Safe because the image parameters are stack variables of the correct type.
325         unsafe { virgl_renderer_execute(&mut query as *mut _ as *mut c_void, query.hdr.size) };
326 
327     ret_to_res(ret)?;
328     Ok(query)
329 }
330 
331 impl VirglRenderer {
init( virglrenderer_flags: VirglRendererFlags, fence_handler: RutabagaFenceHandler, render_server_fd: Option<OwnedDescriptor>, ) -> RutabagaResult<Box<dyn RutabagaComponent>>332     pub fn init(
333         virglrenderer_flags: VirglRendererFlags,
334         fence_handler: RutabagaFenceHandler,
335         render_server_fd: Option<OwnedDescriptor>,
336     ) -> RutabagaResult<Box<dyn RutabagaComponent>> {
337         if cfg!(debug_assertions) {
338             // TODO(b/315870313): Add safety comment
339             #[allow(clippy::undocumented_unsafe_blocks)]
340             let ret = unsafe { libc::dup2(libc::STDOUT_FILENO, libc::STDERR_FILENO) };
341             if ret == -1 {
342                 warn!(
343                     "unable to dup2 stdout to stderr: {}",
344                     SysError::last_os_error()
345                 );
346             }
347         }
348 
349         // virglrenderer is a global state backed library that uses thread bound OpenGL contexts.
350         // Initialize it only once and use the non-send/non-sync Renderer struct to keep things tied
351         // to whichever thread called this function first.
352         static INIT_ONCE: AtomicBool = AtomicBool::new(false);
353         if INIT_ONCE
354             .compare_exchange(false, true, Ordering::Acquire, Ordering::Acquire)
355             .is_err()
356         {
357             return Err(RutabagaError::AlreadyInUse);
358         }
359 
360         // TODO(b/315870313): Add safety comment
361         #[allow(clippy::undocumented_unsafe_blocks)]
362         unsafe {
363             virgl_set_debug_callback(Some(debug_callback))
364         };
365 
366         // Cookie is intentionally never freed because virglrenderer never gets uninitialized.
367         // Otherwise, Resource and Context would become invalid because their lifetime is not tied
368         // to the Renderer instance. Doing so greatly simplifies the ownership for users of this
369         // library.
370         let cookie = Box::into_raw(Box::new(RutabagaCookie {
371             render_server_fd,
372             fence_handler: Some(fence_handler),
373             debug_handler: None,
374         }));
375 
376         // SAFETY:
377         // Safe because a valid cookie and set of callbacks is used and the result is checked for
378         // error.
379         let ret = unsafe {
380             virgl_renderer_init(
381                 cookie as *mut c_void,
382                 virglrenderer_flags.into(),
383                 transmute(VIRGL_RENDERER_CALLBACKS),
384             )
385         };
386 
387         ret_to_res(ret)?;
388         Ok(Box::new(VirglRenderer {}))
389     }
390 
map_info(&self, resource_id: u32) -> RutabagaResult<u32>391     fn map_info(&self, resource_id: u32) -> RutabagaResult<u32> {
392         let mut map_info = 0;
393         // TODO(b/315870313): Add safety comment
394         #[allow(clippy::undocumented_unsafe_blocks)]
395         let ret = unsafe { virgl_renderer_resource_get_map_info(resource_id, &mut map_info) };
396         ret_to_res(ret)?;
397 
398         Ok(map_info | RUTABAGA_MAP_ACCESS_RW)
399     }
400 
query(&self, resource_id: u32) -> RutabagaResult<Resource3DInfo>401     fn query(&self, resource_id: u32) -> RutabagaResult<Resource3DInfo> {
402         let query = export_query(resource_id)?;
403         if query.out_num_fds == 0 {
404             return Err(RutabagaError::Unsupported);
405         }
406 
407         // virglrenderer unfortunately doesn't return the width or height, so map to zero.
408         Ok(Resource3DInfo {
409             width: 0,
410             height: 0,
411             drm_fourcc: query.out_fourcc,
412             strides: query.out_strides,
413             offsets: query.out_offsets,
414             modifier: query.out_modifier,
415             guest_cpu_mappable: false,
416         })
417     }
418 
export_blob(&self, resource_id: u32) -> RutabagaResult<Arc<RutabagaHandle>>419     fn export_blob(&self, resource_id: u32) -> RutabagaResult<Arc<RutabagaHandle>> {
420         let mut fd_type = 0;
421         let mut fd = 0;
422         // TODO(b/315870313): Add safety comment
423         #[allow(clippy::undocumented_unsafe_blocks)]
424         let ret =
425             unsafe { virgl_renderer_resource_export_blob(resource_id, &mut fd_type, &mut fd) };
426         ret_to_res(ret)?;
427 
428         // SAFETY:
429         // Safe because the FD was just returned by a successful virglrenderer
430         // call so it must be valid and owned by us.
431         let handle = unsafe { OwnedDescriptor::from_raw_descriptor(fd) };
432 
433         let handle_type = match fd_type {
434             VIRGL_RENDERER_BLOB_FD_TYPE_DMABUF => RUTABAGA_MEM_HANDLE_TYPE_DMABUF,
435             VIRGL_RENDERER_BLOB_FD_TYPE_SHM => RUTABAGA_MEM_HANDLE_TYPE_SHM,
436             VIRGL_RENDERER_BLOB_FD_TYPE_OPAQUE => RUTABAGA_MEM_HANDLE_TYPE_OPAQUE_FD,
437             _ => {
438                 return Err(RutabagaError::Unsupported);
439             }
440         };
441 
442         Ok(Arc::new(RutabagaHandle {
443             os_handle: handle,
444             handle_type,
445         }))
446     }
447 }
448 
449 impl Drop for VirglRenderer {
drop(&mut self)450     fn drop(&mut self) {
451         // SAFETY:
452         // Safe because virglrenderer is initialized.
453         //
454         // This invalidates all context ids and resource ids.  It is fine because struct Rutabaga
455         // makes sure contexts and resources are dropped before this is reached.  Even if it did
456         // not, virglrenderer is designed to deal with invalid ids safely.
457         unsafe {
458             virgl_renderer_cleanup(null_mut());
459         }
460     }
461 }
462 
463 impl RutabagaComponent for VirglRenderer {
get_capset_info(&self, capset_id: u32) -> (u32, u32)464     fn get_capset_info(&self, capset_id: u32) -> (u32, u32) {
465         let mut version = 0;
466         let mut size = 0;
467         // SAFETY:
468         // Safe because virglrenderer is initialized by now and properly size stack variables are
469         // used for the pointers.
470         unsafe {
471             virgl_renderer_get_cap_set(capset_id, &mut version, &mut size);
472         }
473         (version, size)
474     }
475 
get_capset(&self, capset_id: u32, version: u32) -> Vec<u8>476     fn get_capset(&self, capset_id: u32, version: u32) -> Vec<u8> {
477         let (_, max_size) = self.get_capset_info(capset_id);
478         let mut buf = vec![0u8; max_size as usize];
479         // SAFETY:
480         // Safe because virglrenderer is initialized by now and the given buffer is sized properly
481         // for the given cap id/version.
482         unsafe {
483             virgl_renderer_fill_caps(capset_id, version, buf.as_mut_ptr() as *mut c_void);
484         }
485         buf
486     }
487 
force_ctx_0(&self)488     fn force_ctx_0(&self) {
489         // TODO(b/315870313): Add safety comment
490         #[allow(clippy::undocumented_unsafe_blocks)]
491         unsafe {
492             virgl_renderer_force_ctx_0()
493         };
494     }
495 
create_fence(&mut self, fence: RutabagaFence) -> RutabagaResult<()>496     fn create_fence(&mut self, fence: RutabagaFence) -> RutabagaResult<()> {
497         // TODO(b/315870313): Add safety comment
498         #[allow(clippy::undocumented_unsafe_blocks)]
499         let ret = unsafe { virgl_renderer_create_fence(fence.fence_id as i32, fence.ctx_id) };
500         ret_to_res(ret)
501     }
502 
event_poll(&self)503     fn event_poll(&self) {
504         // TODO(b/315870313): Add safety comment
505         #[allow(clippy::undocumented_unsafe_blocks)]
506         unsafe {
507             virgl_renderer_poll()
508         };
509     }
510 
poll_descriptor(&self) -> Option<OwnedDescriptor>511     fn poll_descriptor(&self) -> Option<OwnedDescriptor> {
512         // SAFETY:
513         // Safe because it can be called anytime and returns -1 in the event of an error.
514         let fd = unsafe { virgl_renderer_get_poll_fd() };
515         if fd >= 0 {
516             let descriptor: RawDescriptor = fd as RawDescriptor;
517             if let Ok(dup_fd) = dup(descriptor) {
518                 return Some(dup_fd);
519             }
520         }
521         None
522     }
523 
create_3d( &self, resource_id: u32, resource_create_3d: ResourceCreate3D, ) -> RutabagaResult<RutabagaResource>524     fn create_3d(
525         &self,
526         resource_id: u32,
527         resource_create_3d: ResourceCreate3D,
528     ) -> RutabagaResult<RutabagaResource> {
529         let mut args = virgl_renderer_resource_create_args {
530             handle: resource_id,
531             target: resource_create_3d.target,
532             format: resource_create_3d.format,
533             bind: resource_create_3d.bind,
534             width: resource_create_3d.width,
535             height: resource_create_3d.height,
536             depth: resource_create_3d.depth,
537             array_size: resource_create_3d.array_size,
538             last_level: resource_create_3d.last_level,
539             nr_samples: resource_create_3d.nr_samples,
540             flags: resource_create_3d.flags,
541         };
542 
543         // SAFETY:
544         // Safe because virglrenderer is initialized by now, and the return value is checked before
545         // returning a new resource. The backing buffers are not supplied with this call.
546         let ret = unsafe { virgl_renderer_resource_create(&mut args, null_mut(), 0) };
547         ret_to_res(ret)?;
548 
549         Ok(RutabagaResource {
550             resource_id,
551             handle: self.export_blob(resource_id).ok(),
552             blob: false,
553             blob_mem: 0,
554             blob_flags: 0,
555             map_info: None,
556             info_2d: None,
557             info_3d: self.query(resource_id).ok(),
558             vulkan_info: None,
559             backing_iovecs: None,
560             component_mask: 1 << (RutabagaComponentType::VirglRenderer as u8),
561             size: 0,
562             mapping: None,
563         })
564     }
565 
attach_backing( &self, resource_id: u32, vecs: &mut Vec<RutabagaIovec>, ) -> RutabagaResult<()>566     fn attach_backing(
567         &self,
568         resource_id: u32,
569         vecs: &mut Vec<RutabagaIovec>,
570     ) -> RutabagaResult<()> {
571         // SAFETY:
572         // Safe because the backing is into guest memory that we store a reference count for.
573         let ret = unsafe {
574             virgl_renderer_resource_attach_iov(
575                 resource_id as i32,
576                 vecs.as_mut_ptr() as *mut iovec,
577                 vecs.len() as i32,
578             )
579         };
580         ret_to_res(ret)
581     }
582 
detach_backing(&self, resource_id: u32)583     fn detach_backing(&self, resource_id: u32) {
584         // SAFETY:
585         // Safe as we don't need the old backing iovecs returned and the reference to the guest
586         // memory can be dropped as it will no longer be needed for this resource.
587         unsafe {
588             virgl_renderer_resource_detach_iov(resource_id as i32, null_mut(), null_mut());
589         }
590     }
591 
unref_resource(&self, resource_id: u32)592     fn unref_resource(&self, resource_id: u32) {
593         // SAFETY:
594         // The resource is safe to unreference destroy because no user of these bindings can still
595         // be holding a reference.
596         unsafe {
597             virgl_renderer_resource_unref(resource_id);
598         }
599     }
600 
transfer_write( &self, ctx_id: u32, resource: &mut RutabagaResource, transfer: Transfer3D, ) -> RutabagaResult<()>601     fn transfer_write(
602         &self,
603         ctx_id: u32,
604         resource: &mut RutabagaResource,
605         transfer: Transfer3D,
606     ) -> RutabagaResult<()> {
607         if transfer.is_empty() {
608             return Ok(());
609         }
610 
611         let mut transfer_box = VirglBox {
612             x: transfer.x,
613             y: transfer.y,
614             z: transfer.z,
615             w: transfer.w,
616             h: transfer.h,
617             d: transfer.d,
618         };
619 
620         // SAFETY:
621         // Safe because only stack variables of the appropriate type are used.
622         let ret = unsafe {
623             virgl_renderer_transfer_write_iov(
624                 resource.resource_id,
625                 ctx_id,
626                 transfer.level as i32,
627                 transfer.stride,
628                 transfer.layer_stride,
629                 &mut transfer_box as *mut VirglBox as *mut virgl_box,
630                 transfer.offset,
631                 null_mut(),
632                 0,
633             )
634         };
635         ret_to_res(ret)
636     }
637 
transfer_read( &self, ctx_id: u32, resource: &mut RutabagaResource, transfer: Transfer3D, buf: Option<IoSliceMut>, ) -> RutabagaResult<()>638     fn transfer_read(
639         &self,
640         ctx_id: u32,
641         resource: &mut RutabagaResource,
642         transfer: Transfer3D,
643         buf: Option<IoSliceMut>,
644     ) -> RutabagaResult<()> {
645         if transfer.is_empty() {
646             return Ok(());
647         }
648 
649         let mut transfer_box = VirglBox {
650             x: transfer.x,
651             y: transfer.y,
652             z: transfer.z,
653             w: transfer.w,
654             h: transfer.h,
655             d: transfer.d,
656         };
657 
658         let mut iov = RutabagaIovec {
659             base: null_mut(),
660             len: 0,
661         };
662 
663         let (iovecs, num_iovecs) = match buf {
664             Some(mut buf) => {
665                 iov.base = buf.as_mut_ptr() as *mut c_void;
666                 iov.len = buf.len();
667                 (&mut iov as *mut RutabagaIovec as *mut iovec, 1)
668             }
669             None => (null_mut(), 0),
670         };
671 
672         // SAFETY:
673         // Safe because only stack variables of the appropriate type are used.
674         let ret = unsafe {
675             virgl_renderer_transfer_read_iov(
676                 resource.resource_id,
677                 ctx_id,
678                 transfer.level,
679                 transfer.stride,
680                 transfer.layer_stride,
681                 &mut transfer_box as *mut VirglBox as *mut virgl_box,
682                 transfer.offset,
683                 iovecs,
684                 num_iovecs,
685             )
686         };
687         ret_to_res(ret)
688     }
689 
690     #[allow(unused_variables)]
create_blob( &mut self, ctx_id: u32, resource_id: u32, resource_create_blob: ResourceCreateBlob, mut iovec_opt: Option<Vec<RutabagaIovec>>, _handle_opt: Option<RutabagaHandle>, ) -> RutabagaResult<RutabagaResource>691     fn create_blob(
692         &mut self,
693         ctx_id: u32,
694         resource_id: u32,
695         resource_create_blob: ResourceCreateBlob,
696         mut iovec_opt: Option<Vec<RutabagaIovec>>,
697         _handle_opt: Option<RutabagaHandle>,
698     ) -> RutabagaResult<RutabagaResource> {
699         let mut iovec_ptr = null_mut();
700         let mut num_iovecs = 0;
701         if let Some(ref mut iovecs) = iovec_opt {
702             iovec_ptr = iovecs.as_mut_ptr();
703             num_iovecs = iovecs.len();
704         }
705 
706         let resource_create_args = virgl_renderer_resource_create_blob_args {
707             res_handle: resource_id,
708             ctx_id,
709             blob_mem: resource_create_blob.blob_mem,
710             blob_flags: resource_create_blob.blob_flags,
711             blob_id: resource_create_blob.blob_id,
712             size: resource_create_blob.size,
713             iovecs: iovec_ptr as *const iovec,
714             num_iovs: num_iovecs as u32,
715         };
716 
717         // TODO(b/315870313): Add safety comment
718         #[allow(clippy::undocumented_unsafe_blocks)]
719         let ret = unsafe { virgl_renderer_resource_create_blob(&resource_create_args) };
720         ret_to_res(ret)?;
721 
722         // TODO(b/244591751): assign vulkan_info to support opaque_fd mapping via Vulkano when
723         // sandboxing (hence external_blob) is enabled.
724         Ok(RutabagaResource {
725             resource_id,
726             handle: self.export_blob(resource_id).ok(),
727             blob: true,
728             blob_mem: resource_create_blob.blob_mem,
729             blob_flags: resource_create_blob.blob_flags,
730             map_info: self.map_info(resource_id).ok(),
731             info_2d: None,
732             info_3d: self.query(resource_id).ok(),
733             vulkan_info: None,
734             backing_iovecs: iovec_opt,
735             component_mask: 1 << (RutabagaComponentType::VirglRenderer as u8),
736             size: resource_create_blob.size,
737             mapping: None,
738         })
739     }
740 
map(&self, resource_id: u32) -> RutabagaResult<RutabagaMapping>741     fn map(&self, resource_id: u32) -> RutabagaResult<RutabagaMapping> {
742         let mut map: *mut c_void = null_mut();
743         let mut size: u64 = 0;
744         // SAFETY:
745         // Safe because virglrenderer wraps and validates use of GL/VK.
746         let ret = unsafe { virgl_renderer_resource_map(resource_id, &mut map, &mut size) };
747         if ret != 0 {
748             return Err(RutabagaError::MappingFailed(ret));
749         }
750 
751         Ok(RutabagaMapping {
752             ptr: map as u64,
753             size,
754         })
755     }
756 
unmap(&self, resource_id: u32) -> RutabagaResult<()>757     fn unmap(&self, resource_id: u32) -> RutabagaResult<()> {
758         // SAFETY:
759         // Safe because virglrenderer is initialized by now.
760         let ret = unsafe { virgl_renderer_resource_unmap(resource_id) };
761         ret_to_res(ret)
762     }
763 
764     #[allow(unused_variables)]
export_fence(&self, fence_id: u64) -> RutabagaResult<RutabagaHandle>765     fn export_fence(&self, fence_id: u64) -> RutabagaResult<RutabagaHandle> {
766         #[cfg(virgl_renderer_unstable)]
767         {
768             let mut fd: i32 = 0;
769             // SAFETY:
770             // Safe because the parameters are stack variables of the correct type.
771             let ret = unsafe { virgl_renderer_export_fence(fence_id, &mut fd) };
772             ret_to_res(ret)?;
773 
774             // SAFETY:
775             // Safe because the FD was just returned by a successful virglrenderer call so it must
776             // be valid and owned by us.
777             let fence = unsafe { OwnedDescriptor::from_raw_descriptor(fd) };
778             Ok(RutabagaHandle {
779                 os_handle: fence,
780                 handle_type: RUTABAGA_FENCE_HANDLE_TYPE_SYNC_FD,
781             })
782         }
783         #[cfg(not(virgl_renderer_unstable))]
784         Err(RutabagaError::Unsupported)
785     }
786 
787     #[allow(unused_variables)]
create_context( &self, ctx_id: u32, context_init: u32, context_name: Option<&str>, _fence_handler: RutabagaFenceHandler, ) -> RutabagaResult<Box<dyn RutabagaContext>>788     fn create_context(
789         &self,
790         ctx_id: u32,
791         context_init: u32,
792         context_name: Option<&str>,
793         _fence_handler: RutabagaFenceHandler,
794     ) -> RutabagaResult<Box<dyn RutabagaContext>> {
795         let mut name: &str = "gpu_renderer";
796         if let Some(name_string) = context_name.filter(|s| !s.is_empty()) {
797             name = name_string;
798         }
799 
800         // SAFETY:
801         // Safe because virglrenderer is initialized by now and the context name is statically
802         // allocated. The return value is checked before returning a new context.
803         let ret = unsafe {
804             match context_init {
805                 0 => virgl_renderer_context_create(
806                     ctx_id,
807                     name.len() as u32,
808                     name.as_ptr() as *const c_char,
809                 ),
810                 _ => virgl_renderer_context_create_with_flags(
811                     ctx_id,
812                     context_init,
813                     name.len() as u32,
814                     name.as_ptr() as *const c_char,
815                 ),
816             }
817         };
818         ret_to_res(ret)?;
819         Ok(Box::new(VirglRendererContext { ctx_id }))
820     }
821 }
822