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