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