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