• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 // Copyright 2021 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 ///! C-bindings for the rutabaga_gfx crate
6 extern crate rutabaga_gfx;
7 
8 use std::convert::TryInto;
9 use std::ffi::CStr;
10 use std::os::raw::c_char;
11 use std::panic::{catch_unwind, AssertUnwindSafe};
12 use std::path::PathBuf;
13 use std::ptr::{copy_nonoverlapping, null_mut};
14 use std::slice::{from_raw_parts, from_raw_parts_mut};
15 
16 use base::{error, FromRawDescriptor, IntoRawDescriptor, SafeDescriptor};
17 use data_model::VolatileSlice;
18 
19 use libc::{iovec, EINVAL, ESRCH};
20 
21 use rutabaga_gfx::*;
22 
23 const NO_ERROR: i32 = 0;
24 
return_result<T>(result: RutabagaResult<T>) -> i3225 fn return_result<T>(result: RutabagaResult<T>) -> i32 {
26     if let Err(e) = result {
27         error!("Received an error {}", e);
28         -EINVAL
29     } else {
30         NO_ERROR
31     }
32 }
33 
34 macro_rules! return_on_error {
35     ($result:expr) => {
36         match $result {
37             Ok(t) => t,
38             Err(e) => {
39                 error!("Received an error {}", e);
40                 return -EINVAL;
41             }
42         }
43     };
44 }
45 
46 const RUTABAGA_COMPONENT_2D: u32 = 1;
47 const RUTABAGA_COMPONENT_VIRGL_RENDERER: u32 = 2;
48 const RUTABAGA_COMPONENT_GFXSTREAM: u32 = 3;
49 const RUTABAGA_COMPONENT_CROSS_DOMAIN: u32 = 4;
50 
51 #[allow(non_camel_case_types)]
52 type rutabaga = Rutabaga;
53 
54 #[allow(non_camel_case_types)]
55 type rutabaga_create_blob = ResourceCreateBlob;
56 
57 #[allow(non_camel_case_types)]
58 type rutabaga_create_3d = ResourceCreate3D;
59 
60 #[allow(non_camel_case_types)]
61 type rutabaga_transfer = Transfer3D;
62 
63 #[allow(non_camel_case_types)]
64 type rutabaga_fence = RutabagaFence;
65 
66 #[repr(C)]
67 #[derive(Copy, Clone)]
68 pub struct rutabaga_iovecs {
69     pub iovecs: *mut iovec,
70     pub num_iovecs: usize,
71 }
72 
73 #[repr(C)]
74 #[derive(Copy, Clone)]
75 pub struct rutabaga_handle {
76     pub os_handle: i32,
77     pub handle_type: u32,
78 }
79 
80 #[repr(C)]
81 pub struct rutabaga_channel {
82     pub channel_name: *const c_char,
83     pub channel_type: u32,
84 }
85 
86 #[repr(C)]
87 pub struct rutabaga_channels {
88     pub channels: *const rutabaga_channel,
89     pub num_channels: usize,
90 }
91 
92 #[allow(non_camel_case_types)]
93 pub type write_fence_cb = extern "C" fn(user_data: u64, fence_data: rutabaga_fence);
94 
95 #[repr(C)]
96 pub struct rutabaga_builder<'a> {
97     pub user_data: u64,
98     pub default_component: u32,
99     pub fence_cb: write_fence_cb,
100     pub channels: Option<&'a rutabaga_channels>,
101 }
102 
create_ffi_fence_handler(user_data: u64, fence_cb: write_fence_cb) -> RutabagaFenceHandler103 fn create_ffi_fence_handler(user_data: u64, fence_cb: write_fence_cb) -> RutabagaFenceHandler {
104     RutabagaFenceClosure::new(move |completed_fence| fence_cb(user_data, completed_fence))
105 }
106 
107 /// # Safety
108 /// - If `(*builder).channels` is not null, the caller must ensure `(*channels).channels` points to
109 ///   a valid array of `struct rutabaga_channel` of size `(*channels).num_channels`.
110 /// - The `channel_name` field of `struct rutabaga_channel` must be a null-terminated C-string.
111 #[no_mangle]
rutabaga_init(builder: &rutabaga_builder, ptr: &mut *mut rutabaga) -> i32112 pub unsafe extern "C" fn rutabaga_init(builder: &rutabaga_builder, ptr: &mut *mut rutabaga) -> i32 {
113     catch_unwind(AssertUnwindSafe(|| {
114         let fence_handler = create_ffi_fence_handler((*builder).user_data, (*builder).fence_cb);
115 
116         let component = match (*builder).default_component {
117             RUTABAGA_COMPONENT_2D => RutabagaComponentType::Rutabaga2D,
118             RUTABAGA_COMPONENT_VIRGL_RENDERER => RutabagaComponentType::VirglRenderer,
119             RUTABAGA_COMPONENT_GFXSTREAM => RutabagaComponentType::Gfxstream,
120             RUTABAGA_COMPONENT_CROSS_DOMAIN => RutabagaComponentType::CrossDomain,
121             _ => {
122                 error!("unknown component type");
123                 return -EINVAL;
124             }
125         };
126 
127         let virglrenderer_flags = VirglRendererFlags::new()
128             .use_egl(true)
129             .use_surfaceless(true)
130             .use_external_blob(true);
131 
132         let gfxstream_flags = GfxstreamFlags::new()
133             .use_egl(true)
134             .use_surfaceless(true)
135             .use_guest_angle(true)
136             .use_syncfd(true)
137             .use_vulkan(true);
138 
139         let mut rutabaga_channels_opt = None;
140         if let Some(channels) = (*builder).channels {
141             let mut rutabaga_channels: Vec<RutabagaChannel> = Vec::new();
142             let channels_slice = from_raw_parts(channels.channels, channels.num_channels);
143 
144             for channel in channels_slice {
145                 let c_str_slice = CStr::from_ptr(channel.channel_name);
146                 let result = c_str_slice.to_str();
147                 let str_slice = return_on_error!(result);
148                 let string = str_slice.to_owned();
149                 let path = PathBuf::from(&string);
150 
151                 rutabaga_channels.push(RutabagaChannel {
152                     base_channel: path,
153                     channel_type: channel.channel_type,
154                 });
155             }
156 
157             rutabaga_channels_opt = Some(rutabaga_channels);
158         }
159         let result = RutabagaBuilder::new(component)
160             .set_virglrenderer_flags(virglrenderer_flags)
161             .set_gfxstream_flags(gfxstream_flags)
162             .set_rutabaga_channels(rutabaga_channels_opt)
163             .build(fence_handler, None);
164 
165         let rtbg = return_on_error!(result);
166         *ptr = Box::into_raw(Box::new(rtbg)) as _;
167         NO_ERROR
168     }))
169     .unwrap_or(-ESRCH)
170 }
171 
172 /// # Safety
173 /// - `ptr` must have been created by `rutabaga_init`.
174 #[no_mangle]
rutabaga_finish(ptr: &mut *mut rutabaga) -> i32175 pub extern "C" fn rutabaga_finish(ptr: &mut *mut rutabaga) -> i32 {
176     catch_unwind(AssertUnwindSafe(|| {
177         unsafe { Box::from_raw(*ptr) };
178         *ptr = null_mut();
179         NO_ERROR
180     }))
181     .unwrap_or(-ESRCH)
182 }
183 
184 #[no_mangle]
rutabaga_get_num_capsets() -> u32185 pub extern "C" fn rutabaga_get_num_capsets() -> u32 {
186     let mut num_capsets = 0;
187 
188     // Cross-domain (like virtio_wl with llvmpipe) is always available.
189     num_capsets += 1;
190 
191     // Three capsets for virgl_renderer
192     #[cfg(feature = "virgl_renderer")]
193     {
194         num_capsets += 3;
195     }
196 
197     // One capset for gfxstream
198     #[cfg(feature = "gfxstream")]
199     {
200         num_capsets += 1;
201     }
202 
203     num_capsets
204 }
205 
206 #[no_mangle]
rutabaga_get_capset_info( ptr: &mut rutabaga, capset_index: u32, capset_id: &mut u32, capset_version: &mut u32, capset_size: &mut u32, ) -> i32207 pub extern "C" fn rutabaga_get_capset_info(
208     ptr: &mut rutabaga,
209     capset_index: u32,
210     capset_id: &mut u32,
211     capset_version: &mut u32,
212     capset_size: &mut u32,
213 ) -> i32 {
214     catch_unwind(AssertUnwindSafe(|| {
215         let result = ptr.get_capset_info(capset_index);
216         let info = return_on_error!(result);
217         *capset_id = info.0;
218         *capset_version = info.1;
219         *capset_size = info.2;
220         NO_ERROR
221     }))
222     .unwrap_or(-ESRCH)
223 }
224 
225 /// # Safety
226 /// - `capset` must point an array of bytes of size `capset_size`.
227 #[no_mangle]
rutabaga_get_capset( ptr: &mut rutabaga, capset_id: u32, version: u32, capset: *mut u8, capset_size: u32, ) -> i32228 pub unsafe extern "C" fn rutabaga_get_capset(
229     ptr: &mut rutabaga,
230     capset_id: u32,
231     version: u32,
232     capset: *mut u8,
233     capset_size: u32,
234 ) -> i32 {
235     catch_unwind(AssertUnwindSafe(|| {
236         let size: usize = capset_size.try_into().map_err(|_e| -EINVAL).unwrap();
237         let result = ptr.get_capset(capset_id, version);
238         let vec = return_on_error!(result);
239         copy_nonoverlapping(vec.as_ptr(), capset, size);
240         NO_ERROR
241     }))
242     .unwrap_or(-ESRCH)
243 }
244 
245 #[no_mangle]
rutabaga_context_create( ptr: &mut rutabaga, ctx_id: u32, context_init: u32, ) -> i32246 pub extern "C" fn rutabaga_context_create(
247     ptr: &mut rutabaga,
248     ctx_id: u32,
249     context_init: u32,
250 ) -> i32 {
251     catch_unwind(AssertUnwindSafe(|| {
252         let result = ptr.create_context(ctx_id, context_init);
253         return_result(result)
254     }))
255     .unwrap_or(-ESRCH)
256 }
257 
258 #[no_mangle]
rutabaga_context_destroy(ptr: &mut rutabaga, ctx_id: u32) -> i32259 pub extern "C" fn rutabaga_context_destroy(ptr: &mut rutabaga, ctx_id: u32) -> i32 {
260     catch_unwind(AssertUnwindSafe(|| {
261         let result = ptr.destroy_context(ctx_id);
262         return_result(result)
263     }))
264     .unwrap_or(-ESRCH)
265 }
266 
267 #[no_mangle]
rutabaga_context_attach_resource( ptr: &mut rutabaga, ctx_id: u32, resource_id: u32, ) -> i32268 pub extern "C" fn rutabaga_context_attach_resource(
269     ptr: &mut rutabaga,
270     ctx_id: u32,
271     resource_id: u32,
272 ) -> i32 {
273     catch_unwind(AssertUnwindSafe(|| {
274         let result = ptr.context_attach_resource(ctx_id, resource_id);
275         return_result(result)
276     }))
277     .unwrap_or(-ESRCH)
278 }
279 
280 #[no_mangle]
rutabaga_context_detach_resource( ptr: &mut rutabaga, ctx_id: u32, resource_id: u32, ) -> i32281 pub extern "C" fn rutabaga_context_detach_resource(
282     ptr: &mut rutabaga,
283     ctx_id: u32,
284     resource_id: u32,
285 ) -> i32 {
286     catch_unwind(AssertUnwindSafe(|| {
287         let result = ptr.context_detach_resource(ctx_id, resource_id);
288         return_result(result)
289     }))
290     .unwrap_or(-ESRCH)
291 }
292 
293 #[no_mangle]
rutabaga_resource_create_3d( ptr: &mut rutabaga, resource_id: u32, create_3d: &rutabaga_create_3d, ) -> i32294 pub extern "C" fn rutabaga_resource_create_3d(
295     ptr: &mut rutabaga,
296     resource_id: u32,
297     create_3d: &rutabaga_create_3d,
298 ) -> i32 {
299     catch_unwind(AssertUnwindSafe(|| {
300         let result = ptr.resource_create_3d(resource_id, *create_3d);
301         return_result(result)
302     }))
303     .unwrap_or(-ESRCH)
304 }
305 
306 /// # Safety
307 /// - If `iovecs` is not null, the caller must ensure `(*iovecs).iovecs` points to a valid array of
308 ///   iovecs of size `(*iovecs).num_iovecs`.
309 /// - Each iovec must point to valid memory starting at `iov_base` with length `iov_len`.
310 /// - Each iovec must valid until the resource's backing is explictly detached or the resource is
311 ///   is unreferenced.
312 #[no_mangle]
rutabaga_resource_attach_backing( ptr: &mut rutabaga, resource_id: u32, iovecs: &rutabaga_iovecs, ) -> i32313 pub unsafe extern "C" fn rutabaga_resource_attach_backing(
314     ptr: &mut rutabaga,
315     resource_id: u32,
316     iovecs: &rutabaga_iovecs,
317 ) -> i32 {
318     catch_unwind(AssertUnwindSafe(|| {
319         let slice = from_raw_parts((*iovecs).iovecs, (*iovecs).num_iovecs);
320         let vecs = slice
321             .iter()
322             .map(|iov| RutabagaIovec {
323                 base: iov.iov_base,
324                 len: iov.iov_len,
325             })
326             .collect();
327 
328         let result = ptr.attach_backing(resource_id, vecs);
329         return_result(result)
330     }))
331     .unwrap_or(-ESRCH)
332 }
333 
334 #[no_mangle]
rutabaga_resource_detach_backing(ptr: &mut rutabaga, resource_id: u32) -> i32335 pub extern "C" fn rutabaga_resource_detach_backing(ptr: &mut rutabaga, resource_id: u32) -> i32 {
336     catch_unwind(AssertUnwindSafe(|| {
337         let result = ptr.detach_backing(resource_id);
338         return_result(result)
339     }))
340     .unwrap_or(-ESRCH)
341 }
342 
343 /// # Safety
344 /// - If `iovecs` is not null, the caller must ensure `(*iovecs).iovecs` points to a valid array of
345 ///   iovecs of size `(*iovecs).num_iovecs`.
346 #[no_mangle]
rutabaga_resource_transfer_read( ptr: &mut rutabaga, ctx_id: u32, resource_id: u32, transfer: &rutabaga_transfer, buf: Option<&iovec>, ) -> i32347 pub unsafe extern "C" fn rutabaga_resource_transfer_read(
348     ptr: &mut rutabaga,
349     ctx_id: u32,
350     resource_id: u32,
351     transfer: &rutabaga_transfer,
352     buf: Option<&iovec>,
353 ) -> i32 {
354     catch_unwind(AssertUnwindSafe(|| {
355         let mut slice_opt = None;
356         if let Some(iovec) = buf {
357             slice_opt = Some(VolatileSlice::from_raw_parts(
358                 iovec.iov_base as *mut u8,
359                 iovec.iov_len,
360             ));
361         }
362 
363         let result = ptr.transfer_read(ctx_id, resource_id, *transfer, slice_opt);
364         return_result(result)
365     }))
366     .unwrap_or(-ESRCH)
367 }
368 
369 #[no_mangle]
rutabaga_resource_transfer_write( ptr: &mut rutabaga, ctx_id: u32, resource_id: u32, transfer: &rutabaga_transfer, ) -> i32370 pub extern "C" fn rutabaga_resource_transfer_write(
371     ptr: &mut rutabaga,
372     ctx_id: u32,
373     resource_id: u32,
374     transfer: &rutabaga_transfer,
375 ) -> i32 {
376     catch_unwind(AssertUnwindSafe(|| {
377         let result = ptr.transfer_write(ctx_id, resource_id, *transfer);
378         return_result(result)
379     }))
380     .unwrap_or(-ESRCH)
381 }
382 
383 /// # Safety
384 /// - If `iovecs` is not null, the caller must ensure `(*iovecs).iovecs` points to a valid array of
385 ///   iovecs of size `(*iovecs).num_iovecs`.
386 /// - If `handle` is not null, the caller must ensure it is a valid OS-descriptor.  Ownership is
387 ///   transfered to rutabaga.
388 /// - Each iovec must valid until the resource's backing is explictly detached or the resource is
389 ///   is unreferenced.
390 #[no_mangle]
rutabaga_resource_create_blob( ptr: &mut rutabaga, ctx_id: u32, resource_id: u32, create_blob: &rutabaga_create_blob, iovecs: Option<&rutabaga_iovecs>, handle: Option<&rutabaga_handle>, ) -> i32391 pub unsafe extern "C" fn rutabaga_resource_create_blob(
392     ptr: &mut rutabaga,
393     ctx_id: u32,
394     resource_id: u32,
395     create_blob: &rutabaga_create_blob,
396     iovecs: Option<&rutabaga_iovecs>,
397     handle: Option<&rutabaga_handle>,
398 ) -> i32 {
399     catch_unwind(AssertUnwindSafe(|| {
400         let mut iovecs_opt: Option<Vec<RutabagaIovec>> = None;
401         if let Some(iovs) = iovecs {
402             let slice = from_raw_parts((*iovs).iovecs, (*iovs).num_iovecs);
403             let vecs = slice
404                 .iter()
405                 .map(|iov| RutabagaIovec {
406                     base: iov.iov_base,
407                     len: iov.iov_len,
408                 })
409                 .collect();
410             iovecs_opt = Some(vecs);
411         }
412 
413         let mut handle_opt: Option<RutabagaHandle> = None;
414         if let Some(hnd) = handle {
415             handle_opt = Some(RutabagaHandle {
416                 os_handle: SafeDescriptor::from_raw_descriptor((*hnd).os_handle),
417                 handle_type: (*hnd).handle_type,
418             });
419         }
420 
421         let result =
422             ptr.resource_create_blob(ctx_id, resource_id, *create_blob, iovecs_opt, handle_opt);
423 
424         return_result(result)
425     }))
426     .unwrap_or(-ESRCH)
427 }
428 
429 #[no_mangle]
rutabaga_resource_unref(ptr: &mut rutabaga, resource_id: u32) -> i32430 pub extern "C" fn rutabaga_resource_unref(ptr: &mut rutabaga, resource_id: u32) -> i32 {
431     catch_unwind(AssertUnwindSafe(|| {
432         let result = ptr.unref_resource(resource_id);
433         return_result(result)
434     }))
435     .unwrap_or(-ESRCH)
436 }
437 
438 /// # Safety
439 /// Caller owns raw descriptor on success and is responsible for closing it.
440 #[no_mangle]
rutabaga_resource_export_blob( ptr: &mut rutabaga, resource_id: u32, handle: &mut rutabaga_handle, ) -> i32441 pub extern "C" fn rutabaga_resource_export_blob(
442     ptr: &mut rutabaga,
443     resource_id: u32,
444     handle: &mut rutabaga_handle,
445 ) -> i32 {
446     catch_unwind(AssertUnwindSafe(|| {
447         let result = ptr.export_blob(resource_id);
448         let hnd = return_on_error!(result);
449 
450         (*handle).handle_type = hnd.handle_type;
451         (*handle).os_handle = hnd.os_handle.into_raw_descriptor();
452         NO_ERROR
453     }))
454     .unwrap_or(-ESRCH)
455 }
456 
457 #[no_mangle]
rutabaga_resource_map_info( ptr: &mut rutabaga, resource_id: u32, map_info: &mut u32, ) -> i32458 pub extern "C" fn rutabaga_resource_map_info(
459     ptr: &mut rutabaga,
460     resource_id: u32,
461     map_info: &mut u32,
462 ) -> i32 {
463     catch_unwind(AssertUnwindSafe(|| {
464         let result = ptr.map_info(resource_id);
465         *map_info = return_on_error!(result);
466         NO_ERROR
467     }))
468     .unwrap_or(-ESRCH)
469 }
470 
471 /// # Safety
472 /// - `commands` must point to a contiguous memory region of `size` bytes.
473 #[no_mangle]
rutabaga_submit_command( ptr: &mut rutabaga, ctx_id: u32, commands: *mut u8, size: usize, ) -> i32474 pub unsafe extern "C" fn rutabaga_submit_command(
475     ptr: &mut rutabaga,
476     ctx_id: u32,
477     commands: *mut u8,
478     size: usize,
479 ) -> i32 {
480     catch_unwind(AssertUnwindSafe(|| {
481         let cmd_slice = from_raw_parts_mut(commands, size);
482         let result = ptr.submit_command(ctx_id, cmd_slice);
483         return_result(result)
484     }))
485     .unwrap_or(-ESRCH)
486 }
487 
488 #[no_mangle]
rutabaga_create_fence(ptr: &mut rutabaga, fence: &rutabaga_fence) -> i32489 pub extern "C" fn rutabaga_create_fence(ptr: &mut rutabaga, fence: &rutabaga_fence) -> i32 {
490     catch_unwind(AssertUnwindSafe(|| {
491         let result = ptr.create_fence(*fence);
492         return_result(result)
493     }))
494     .unwrap_or(-ESRCH)
495 }
496