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