• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 // Copyright 2020 The Chromium OS Authors. All rights reserved.
2 // Use of this source code is governed by a BSD-style license that can be
3 // found in the LICENSE file.
4 
5 //! 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::cell::RefCell;
11 use std::ffi::CString;
12 use std::mem::{size_of, transmute};
13 use std::os::raw::{c_char, c_void};
14 use std::ptr::null_mut;
15 use std::rc::Rc;
16 use std::sync::atomic::{AtomicBool, Ordering};
17 use std::sync::Arc;
18 
19 use base::{
20     warn, Error as SysError, ExternalMapping, ExternalMappingError, ExternalMappingResult,
21     FromRawDescriptor, SafeDescriptor,
22 };
23 
24 use crate::generated::virgl_renderer_bindings::*;
25 use crate::renderer_utils::*;
26 use crate::rutabaga_core::{RutabagaComponent, RutabagaContext, RutabagaResource};
27 use crate::rutabaga_utils::*;
28 
29 use data_model::VolatileSlice;
30 
31 use libc::close;
32 
33 type Query = virgl_renderer_export_query;
34 
35 /// The virtio-gpu backend state tracker which supports accelerated rendering.
36 pub struct VirglRenderer {
37     fence_state: Rc<RefCell<FenceState>>,
38 }
39 
40 struct VirglRendererContext {
41     ctx_id: u32,
42 }
43 
44 impl RutabagaContext for VirglRendererContext {
submit_cmd(&mut self, commands: &mut [u8]) -> RutabagaResult<()>45     fn submit_cmd(&mut self, commands: &mut [u8]) -> RutabagaResult<()> {
46         if commands.len() % size_of::<u32>() != 0 {
47             return Err(RutabagaError::InvalidCommandSize(commands.len()));
48         }
49         let dword_count = (commands.len() / size_of::<u32>()) as i32;
50         // Safe because the context and buffer are valid and virglrenderer will have been
51         // initialized if there are Context instances.
52         let ret = unsafe {
53             virgl_renderer_submit_cmd(
54                 commands.as_mut_ptr() as *mut c_void,
55                 self.ctx_id as i32,
56                 dword_count,
57             )
58         };
59         ret_to_res(ret)
60     }
61 
attach(&mut self, resource: &mut RutabagaResource)62     fn attach(&mut self, resource: &mut RutabagaResource) {
63         // The context id and resource id must be valid because the respective instances ensure
64         // their lifetime.
65         unsafe {
66             virgl_renderer_ctx_attach_resource(self.ctx_id as i32, resource.resource_id as i32);
67         }
68     }
69 
detach(&mut self, resource: &RutabagaResource)70     fn detach(&mut self, resource: &RutabagaResource) {
71         // The context id and resource id must be valid because the respective instances ensure
72         // their lifetime.
73         unsafe {
74             virgl_renderer_ctx_detach_resource(self.ctx_id as i32, resource.resource_id as i32);
75         }
76     }
77 }
78 
79 impl Drop for VirglRendererContext {
drop(&mut self)80     fn drop(&mut self) {
81         // The context is safe to destroy because nothing else can be referencing it.
82         unsafe {
83             virgl_renderer_context_destroy(self.ctx_id);
84         }
85     }
86 }
87 
88 #[cfg(any(target_arch = "x86", target_arch = "x86_64"))]
debug_callback(fmt: *const ::std::os::raw::c_char, ap: *mut __va_list_tag)89 extern "C" fn debug_callback(fmt: *const ::std::os::raw::c_char, ap: *mut __va_list_tag) {
90     let len: u32 = 256;
91     let mut c_str = CString::new(vec![' ' as u8; len as usize]).unwrap();
92     unsafe {
93         let mut varargs = __va_list_tag {
94             gp_offset: (*ap).gp_offset,
95             fp_offset: (*ap).fp_offset,
96             overflow_arg_area: (*ap).overflow_arg_area,
97             reg_save_area: (*ap).reg_save_area,
98         };
99 
100         let raw = c_str.into_raw();
101         vsnprintf(raw, len.into(), fmt, &mut varargs);
102         c_str = CString::from_raw(raw);
103     }
104     base::debug!("{}", c_str.to_string_lossy());
105 }
106 
107 const VIRGL_RENDERER_CALLBACKS: &virgl_renderer_callbacks = &virgl_renderer_callbacks {
108     version: 1,
109     write_fence: Some(write_fence),
110     create_gl_context: None,
111     destroy_gl_context: None,
112     make_current: None,
113     get_drm_fd: None,
114 };
115 
116 /// Retrieves metadata suitable for export about this resource. If "export_fd" is true,
117 /// performs an export of this resource so that it may be imported by other processes.
export_query(resource_id: u32) -> RutabagaResult<Query>118 fn export_query(resource_id: u32) -> RutabagaResult<Query> {
119     let mut query: Query = Default::default();
120     query.hdr.stype = VIRGL_RENDERER_STRUCTURE_TYPE_EXPORT_QUERY;
121     query.hdr.stype_version = 0;
122     query.hdr.size = size_of::<Query>() as u32;
123     query.in_resource_id = resource_id;
124     query.in_export_fds = 0;
125 
126     // Safe because the image parameters are stack variables of the correct type.
127     let ret =
128         unsafe { virgl_renderer_execute(&mut query as *mut _ as *mut c_void, query.hdr.size) };
129 
130     ret_to_res(ret)?;
131     Ok(query)
132 }
133 
134 #[allow(unused_variables)]
map_func(resource_id: u32) -> ExternalMappingResult<(u64, usize)>135 fn map_func(resource_id: u32) -> ExternalMappingResult<(u64, usize)> {
136     #[cfg(feature = "virgl_renderer_next")]
137     {
138         let mut map: *mut c_void = null_mut();
139         let map_ptr: *mut *mut c_void = &mut map;
140         let mut size: u64 = 0;
141         // Safe because virglrenderer wraps and validates use of GL/VK.
142         let ret = unsafe { virgl_renderer_resource_map(resource_id, map_ptr, &mut size) };
143         if ret != 0 {
144             return Err(ExternalMappingError::LibraryError(ret));
145         }
146 
147         Ok((map as u64, size as usize))
148     }
149     #[cfg(not(feature = "virgl_renderer_next"))]
150     Err(ExternalMappingError::Unsupported)
151 }
152 
153 #[allow(unused_variables)]
unmap_func(resource_id: u32)154 fn unmap_func(resource_id: u32) {
155     #[cfg(feature = "virgl_renderer_next")]
156     {
157         unsafe {
158             // Usually, process_gpu_command forces ctx0 when processing a virtio-gpu command.
159             // During VM shutdown, the KVM thread releases mappings without virtio-gpu being
160             // involved, so force ctx0 here. It's a no-op when the ctx is already 0, so there's
161             // little performance loss during normal VM operation.
162             virgl_renderer_force_ctx_0();
163             virgl_renderer_resource_unmap(resource_id);
164         }
165     }
166 }
167 
168 impl VirglRenderer {
init( virglrenderer_flags: VirglRendererFlags, ) -> RutabagaResult<Box<dyn RutabagaComponent>>169     pub fn init(
170         virglrenderer_flags: VirglRendererFlags,
171     ) -> RutabagaResult<Box<dyn RutabagaComponent>> {
172         if cfg!(debug_assertions) {
173             let ret = unsafe { libc::dup2(libc::STDOUT_FILENO, libc::STDERR_FILENO) };
174             if ret == -1 {
175                 warn!("unable to dup2 stdout to stderr: {}", SysError::last());
176             }
177         }
178 
179         // virglrenderer is a global state backed library that uses thread bound OpenGL contexts.
180         // Initialize it only once and use the non-send/non-sync Renderer struct to keep things tied
181         // to whichever thread called this function first.
182         static INIT_ONCE: AtomicBool = AtomicBool::new(false);
183         if INIT_ONCE
184             .compare_exchange(false, true, Ordering::Acquire, Ordering::Acquire)
185             .is_err()
186         {
187             return Err(RutabagaError::AlreadyInUse);
188         }
189 
190         // Cookie is intentionally never freed because virglrenderer never gets uninitialized.
191         // Otherwise, Resource and Context would become invalid because their lifetime is not tied
192         // to the Renderer instance. Doing so greatly simplifies the ownership for users of this
193         // library.
194 
195         let fence_state = Rc::new(RefCell::new(FenceState { latest_fence: 0 }));
196 
197         let cookie: *mut VirglCookie = Box::into_raw(Box::new(VirglCookie {
198             fence_state: Rc::clone(&fence_state),
199         }));
200 
201         #[cfg(any(target_arch = "x86", target_arch = "x86_64"))]
202         unsafe {
203             virgl_set_debug_callback(Some(debug_callback))
204         };
205 
206         // Safe because a valid cookie and set of callbacks is used and the result is checked for
207         // error.
208         let ret = unsafe {
209             virgl_renderer_init(
210                 cookie as *mut c_void,
211                 virglrenderer_flags.into(),
212                 transmute(VIRGL_RENDERER_CALLBACKS),
213             )
214         };
215 
216         ret_to_res(ret)?;
217         Ok(Box::new(VirglRenderer { fence_state }))
218     }
219 
220     #[allow(unused_variables)]
map_info(&self, resource_id: u32) -> RutabagaResult<u32>221     fn map_info(&self, resource_id: u32) -> RutabagaResult<u32> {
222         #[cfg(feature = "virgl_renderer_next")]
223         {
224             let mut map_info = 0;
225             let ret =
226                 unsafe { virgl_renderer_resource_get_map_info(resource_id as u32, &mut map_info) };
227             ret_to_res(ret)?;
228 
229             Ok(map_info)
230         }
231         #[cfg(not(feature = "virgl_renderer_next"))]
232         Err(RutabagaError::Unsupported)
233     }
234 
query(&self, resource_id: u32) -> RutabagaResult<Resource3DInfo>235     fn query(&self, resource_id: u32) -> RutabagaResult<Resource3DInfo> {
236         let query = export_query(resource_id)?;
237         if query.out_num_fds == 0 {
238             return Err(RutabagaError::Unsupported);
239         }
240 
241         // virglrenderer unfortunately doesn't return the width or height, so map to zero.
242         Ok(Resource3DInfo {
243             width: 0,
244             height: 0,
245             drm_fourcc: query.out_fourcc,
246             strides: query.out_strides,
247             offsets: query.out_offsets,
248             modifier: query.out_modifier,
249         })
250     }
251 
252     #[allow(unused_variables)]
export_blob(&self, resource_id: u32) -> RutabagaResult<Arc<RutabagaHandle>>253     fn export_blob(&self, resource_id: u32) -> RutabagaResult<Arc<RutabagaHandle>> {
254         #[cfg(feature = "virgl_renderer_next")]
255         {
256             let mut fd_type = 0;
257             let mut fd = 0;
258             let ret = unsafe {
259                 virgl_renderer_resource_export_blob(resource_id as u32, &mut fd_type, &mut fd)
260             };
261             ret_to_res(ret)?;
262 
263             /* Only support dma-bufs until someone wants opaque fds too. */
264             if fd_type != VIRGL_RENDERER_BLOB_FD_TYPE_DMABUF {
265                 // Safe because the FD was just returned by a successful virglrenderer
266                 // call so it must be valid and owned by us.
267                 unsafe { close(fd) };
268                 return Err(RutabagaError::Unsupported);
269             }
270 
271             let dmabuf = unsafe { SafeDescriptor::from_raw_descriptor(fd) };
272             Ok(Arc::new(RutabagaHandle {
273                 os_handle: dmabuf,
274                 handle_type: RUTABAGA_MEM_HANDLE_TYPE_DMABUF,
275             }))
276         }
277         #[cfg(not(feature = "virgl_renderer_next"))]
278         Err(RutabagaError::Unsupported)
279     }
280 }
281 
282 impl RutabagaComponent for VirglRenderer {
get_capset_info(&self, capset_id: u32) -> (u32, u32)283     fn get_capset_info(&self, capset_id: u32) -> (u32, u32) {
284         let mut version = 0;
285         let mut size = 0;
286         // Safe because virglrenderer is initialized by now and properly size stack variables are
287         // used for the pointers.
288         unsafe {
289             virgl_renderer_get_cap_set(capset_id, &mut version, &mut size);
290         }
291         (version, size)
292     }
293 
get_capset(&self, capset_id: u32, version: u32) -> Vec<u8>294     fn get_capset(&self, capset_id: u32, version: u32) -> Vec<u8> {
295         let (_, max_size) = self.get_capset_info(capset_id);
296         let mut buf = vec![0u8; max_size as usize];
297         // Safe because virglrenderer is initialized by now and the given buffer is sized properly
298         // for the given cap id/version.
299         unsafe {
300             virgl_renderer_fill_caps(capset_id, version, buf.as_mut_ptr() as *mut c_void);
301         }
302         buf
303     }
304 
force_ctx_0(&self)305     fn force_ctx_0(&self) {
306         unsafe { virgl_renderer_force_ctx_0() };
307     }
308 
create_fence(&mut self, fence_data: RutabagaFenceData) -> RutabagaResult<()>309     fn create_fence(&mut self, fence_data: RutabagaFenceData) -> RutabagaResult<()> {
310         let ret =
311             unsafe { virgl_renderer_create_fence(fence_data.fence_id as i32, fence_data.ctx_id) };
312         ret_to_res(ret)
313     }
314 
poll(&self) -> u32315     fn poll(&self) -> u32 {
316         unsafe { virgl_renderer_poll() };
317         self.fence_state.borrow().latest_fence
318     }
319 
create_3d( &self, resource_id: u32, resource_create_3d: ResourceCreate3D, ) -> RutabagaResult<RutabagaResource>320     fn create_3d(
321         &self,
322         resource_id: u32,
323         resource_create_3d: ResourceCreate3D,
324     ) -> RutabagaResult<RutabagaResource> {
325         let mut args = virgl_renderer_resource_create_args {
326             handle: resource_id,
327             target: resource_create_3d.target,
328             format: resource_create_3d.format,
329             bind: resource_create_3d.bind,
330             width: resource_create_3d.width,
331             height: resource_create_3d.height,
332             depth: resource_create_3d.depth,
333             array_size: resource_create_3d.array_size,
334             last_level: resource_create_3d.last_level,
335             nr_samples: resource_create_3d.nr_samples,
336             flags: resource_create_3d.flags,
337         };
338 
339         // Safe because virglrenderer is initialized by now, and the return value is checked before
340         // returning a new resource. The backing buffers are not supplied with this call.
341         let ret = unsafe { virgl_renderer_resource_create(&mut args, null_mut(), 0) };
342         ret_to_res(ret)?;
343 
344         Ok(RutabagaResource {
345             resource_id,
346             handle: self.export_blob(resource_id).ok(),
347             blob: false,
348             blob_mem: 0,
349             blob_flags: 0,
350             map_info: None,
351             info_2d: None,
352             info_3d: self.query(resource_id).ok(),
353             vulkan_info: None,
354             backing_iovecs: None,
355         })
356     }
357 
attach_backing( &self, resource_id: u32, vecs: &mut Vec<RutabagaIovec>, ) -> RutabagaResult<()>358     fn attach_backing(
359         &self,
360         resource_id: u32,
361         vecs: &mut Vec<RutabagaIovec>,
362     ) -> RutabagaResult<()> {
363         // Safe because the backing is into guest memory that we store a reference count for.
364         let ret = unsafe {
365             virgl_renderer_resource_attach_iov(
366                 resource_id as i32,
367                 vecs.as_mut_ptr() as *mut iovec,
368                 vecs.len() as i32,
369             )
370         };
371         ret_to_res(ret)
372     }
373 
detach_backing(&self, resource_id: u32)374     fn detach_backing(&self, resource_id: u32) {
375         // Safe as we don't need the old backing iovecs returned and the reference to the guest
376         // memory can be dropped as it will no longer be needed for this resource.
377         unsafe {
378             virgl_renderer_resource_detach_iov(resource_id as i32, null_mut(), null_mut());
379         }
380     }
381 
unref_resource(&self, resource_id: u32)382     fn unref_resource(&self, resource_id: u32) {
383         // The resource is safe to unreference destroy because no user of these bindings can still
384         // be holding a reference.
385         unsafe {
386             virgl_renderer_resource_unref(resource_id);
387         }
388     }
389 
transfer_write( &self, ctx_id: u32, resource: &mut RutabagaResource, transfer: Transfer3D, ) -> RutabagaResult<()>390     fn transfer_write(
391         &self,
392         ctx_id: u32,
393         resource: &mut RutabagaResource,
394         transfer: Transfer3D,
395     ) -> RutabagaResult<()> {
396         if transfer.is_empty() {
397             return Ok(());
398         }
399 
400         let mut transfer_box = VirglBox {
401             x: transfer.x,
402             y: transfer.y,
403             z: transfer.z,
404             w: transfer.w,
405             h: transfer.h,
406             d: transfer.d,
407         };
408 
409         // Safe because only stack variables of the appropriate type are used.
410         let ret = unsafe {
411             virgl_renderer_transfer_write_iov(
412                 resource.resource_id,
413                 ctx_id,
414                 transfer.level as i32,
415                 transfer.stride,
416                 transfer.layer_stride,
417                 &mut transfer_box as *mut VirglBox as *mut virgl_box,
418                 transfer.offset,
419                 null_mut(),
420                 0,
421             )
422         };
423         ret_to_res(ret)
424     }
425 
transfer_read( &self, ctx_id: u32, resource: &mut RutabagaResource, transfer: Transfer3D, buf: Option<VolatileSlice>, ) -> RutabagaResult<()>426     fn transfer_read(
427         &self,
428         ctx_id: u32,
429         resource: &mut RutabagaResource,
430         transfer: Transfer3D,
431         buf: Option<VolatileSlice>,
432     ) -> RutabagaResult<()> {
433         if transfer.is_empty() {
434             return Ok(());
435         }
436 
437         let mut transfer_box = VirglBox {
438             x: transfer.x,
439             y: transfer.y,
440             z: transfer.z,
441             w: transfer.w,
442             h: transfer.h,
443             d: transfer.d,
444         };
445 
446         let mut iov = RutabagaIovec {
447             base: null_mut(),
448             len: 0,
449         };
450 
451         let (iovecs, num_iovecs) = match buf {
452             Some(buf) => {
453                 iov.base = buf.as_ptr() as *mut c_void;
454                 iov.len = buf.size() as usize;
455                 (&mut iov as *mut RutabagaIovec as *mut iovec, 1)
456             }
457             None => (null_mut(), 0),
458         };
459 
460         // Safe because only stack variables of the appropriate type are used.
461         let ret = unsafe {
462             virgl_renderer_transfer_read_iov(
463                 resource.resource_id,
464                 ctx_id,
465                 transfer.level,
466                 transfer.stride,
467                 transfer.layer_stride,
468                 &mut transfer_box as *mut VirglBox as *mut virgl_box,
469                 transfer.offset,
470                 iovecs,
471                 num_iovecs,
472             )
473         };
474         ret_to_res(ret)
475     }
476 
477     #[allow(unused_variables)]
create_blob( &mut self, ctx_id: u32, resource_id: u32, resource_create_blob: ResourceCreateBlob, mut iovec_opt: Option<Vec<RutabagaIovec>>, ) -> RutabagaResult<RutabagaResource>478     fn create_blob(
479         &mut self,
480         ctx_id: u32,
481         resource_id: u32,
482         resource_create_blob: ResourceCreateBlob,
483         mut iovec_opt: Option<Vec<RutabagaIovec>>,
484     ) -> RutabagaResult<RutabagaResource> {
485         #[cfg(feature = "virgl_renderer_next")]
486         {
487             let mut iovec_ptr = null_mut();
488             let mut num_iovecs = 0;
489             if let Some(ref mut iovecs) = iovec_opt {
490                 iovec_ptr = iovecs.as_mut_ptr();
491                 num_iovecs = iovecs.len();
492             }
493 
494             let resource_create_args = virgl_renderer_resource_create_blob_args {
495                 res_handle: resource_id,
496                 ctx_id,
497                 blob_mem: resource_create_blob.blob_mem,
498                 blob_flags: resource_create_blob.blob_flags,
499                 blob_id: resource_create_blob.blob_id,
500                 size: resource_create_blob.size,
501                 iovecs: iovec_ptr as *const iovec,
502                 num_iovs: num_iovecs as u32,
503             };
504 
505             let ret = unsafe { virgl_renderer_resource_create_blob(&resource_create_args) };
506             ret_to_res(ret)?;
507 
508             Ok(RutabagaResource {
509                 resource_id,
510                 handle: self.export_blob(resource_id).ok(),
511                 blob: true,
512                 blob_mem: resource_create_blob.blob_mem,
513                 blob_flags: resource_create_blob.blob_flags,
514                 map_info: self.map_info(resource_id).ok(),
515                 info_2d: None,
516                 info_3d: self.query(resource_id).ok(),
517                 vulkan_info: None,
518                 backing_iovecs: iovec_opt,
519             })
520         }
521         #[cfg(not(feature = "virgl_renderer_next"))]
522         Err(RutabagaError::Unsupported)
523     }
524 
map(&self, resource_id: u32) -> RutabagaResult<ExternalMapping>525     fn map(&self, resource_id: u32) -> RutabagaResult<ExternalMapping> {
526         let map_result = unsafe { ExternalMapping::new(resource_id, map_func, unmap_func) };
527         match map_result {
528             Ok(mapping) => Ok(mapping),
529             Err(e) => Err(RutabagaError::MappingFailed(e)),
530         }
531     }
532 
533     #[allow(unused_variables)]
export_fence(&self, fence_id: u32) -> RutabagaResult<RutabagaHandle>534     fn export_fence(&self, fence_id: u32) -> RutabagaResult<RutabagaHandle> {
535         #[cfg(feature = "virgl_renderer_next")]
536         {
537             // Safe because the parameters are stack variables of the correct type.
538             let mut fd: i32 = 0;
539             let ret = unsafe { virgl_renderer_export_fence(fence_id, &mut fd) };
540             ret_to_res(ret)?;
541 
542             // Safe because the FD was just returned by a successful virglrenderer call so it must
543             // be valid and owned by us.
544             let fence = unsafe { SafeDescriptor::from_raw_descriptor(fd) };
545             Ok(RutabagaHandle {
546                 os_handle: fence,
547                 handle_type: RUTABAGA_FENCE_HANDLE_TYPE_SYNC_FD,
548             })
549         }
550         #[cfg(not(feature = "virgl_renderer_next"))]
551         Err(RutabagaError::Unsupported)
552     }
553 
554     #[allow(unused_variables)]
create_context( &self, ctx_id: u32, context_init: u32, ) -> RutabagaResult<Box<dyn RutabagaContext>>555     fn create_context(
556         &self,
557         ctx_id: u32,
558         context_init: u32,
559     ) -> RutabagaResult<Box<dyn RutabagaContext>> {
560         const CONTEXT_NAME: &[u8] = b"gpu_renderer";
561         // Safe because virglrenderer is initialized by now and the context name is statically
562         // allocated. The return value is checked before returning a new context.
563         let ret = unsafe {
564             #[cfg(feature = "virgl_renderer_next")]
565             match context_init {
566                 0 => virgl_renderer_context_create(
567                     ctx_id,
568                     CONTEXT_NAME.len() as u32,
569                     CONTEXT_NAME.as_ptr() as *const c_char,
570                 ),
571                 _ => virgl_renderer_context_create_with_flags(
572                     ctx_id,
573                     context_init,
574                     CONTEXT_NAME.len() as u32,
575                     CONTEXT_NAME.as_ptr() as *const c_char,
576                 ),
577             }
578             #[cfg(not(feature = "virgl_renderer_next"))]
579             virgl_renderer_context_create(
580                 ctx_id,
581                 CONTEXT_NAME.len() as u32,
582                 CONTEXT_NAME.as_ptr() as *const c_char,
583             )
584         };
585         ret_to_res(ret)?;
586         Ok(Box::new(VirglRendererContext { ctx_id }))
587     }
588 }
589