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