About Social Code
aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorGurchetan Singh <gurchetansingh@chromium.org>2025-05-27 08:16:54 -0700
committerMarge Bot <marge-bot@fdo.invalid>2025-06-17 22:28:55 +0000
commitf2b07903abaf348151aad02116585367d7d42f51 (patch)
treea0b39c0e892145c48b59a88c7063cef0fb904598
parentc00027b46fe2ceb7365dabfc421d5fffc90ba713 (diff)
mesa: import virtgpu_kumquat_ffi
This adds a frontend to C-FFI to VirtGpuKumquat, to be used with gfxstream. This allows testing gfxstream, without a virtual machine. Reviewed-by: Aaron Ruby <aruby@qnx.com> Part-of: <https://gitlab.freedesktop.org/mesa/mesa/-/merge_requests/35210>
-rw-r--r--src/virtio/virtgpu_kumquat_ffi/include/.clang-format14
-rw-r--r--src/virtio/virtgpu_kumquat_ffi/include/virtgpu_kumquat_ffi.h285
-rw-r--r--src/virtio/virtgpu_kumquat_ffi/lib.rs424
-rw-r--r--src/virtio/virtgpu_kumquat_ffi/meson.build27
4 files changed, 750 insertions, 0 deletions
diff --git a/src/virtio/virtgpu_kumquat_ffi/include/.clang-format b/src/virtio/virtgpu_kumquat_ffi/include/.clang-format
new file mode 100644
index 00000000000..fffb6d3242c
--- /dev/null
+++ b/src/virtio/virtgpu_kumquat_ffi/include/.clang-format
@@ -0,0 +1,14 @@
+# Copyright 2025 Google
+# SPDX license identifier: MIT
+
+BasedOnStyle: LLVM
+AllowShortFunctionsOnASingleLine: None
+AllowShortIfStatementsOnASingleLine: false
+AllowShortLoopsOnASingleLine: false
+BreakBeforeBraces: Linux
+ColumnLimit: 100
+IndentWidth: 4
+TabWidth: 4
+UseTab: Never
+Cpp11BracedListStyle: false
+IndentCaseLabels: false
diff --git a/src/virtio/virtgpu_kumquat_ffi/include/virtgpu_kumquat_ffi.h b/src/virtio/virtgpu_kumquat_ffi/include/virtgpu_kumquat_ffi.h
new file mode 100644
index 00000000000..dc225dc9849
--- /dev/null
+++ b/src/virtio/virtgpu_kumquat_ffi/include/virtgpu_kumquat_ffi.h
@@ -0,0 +1,285 @@
+/*
+ * Copyright 2025 Google
+ * SPDX License identifier: MIT
+ */
+
+#include <stdarg.h>
+#include <stdbool.h>
+#include <stdint.h>
+#include <stdlib.h>
+
+#ifndef VIRTGPU_KUMQUAT_FFI_H
+#define VIRTGPU_KUMQUAT_FFI_H
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+struct virtgpu_kumquat;
+
+struct drm_kumquat_map {
+ uint32_t bo_handle;
+
+ // out
+ void *ptr;
+ uint64_t size;
+};
+
+#define VIRTGPU_KUMQUAT_EXECBUF_SYNCOBJ_RESET 0x01
+#define VIRTGPU_KUMQUAT_EXECBUF_SYNCOBJ_FLAGS (VIRTGPU_KUMQUAT_EXECBUF_SYNCOBJ_RESET | 0)
+struct drm_kumquat_execbuffer_syncobj {
+ uint32_t handle;
+ uint32_t flags;
+ uint64_t point;
+};
+
+#define VIRTGPU_KUMQUAT_EXECBUF_FENCE_HANDLE_IN 0x01
+#define VIRTGPU_KUMQUAT_EXECBUF_FENCE_HANDLE_OUT 0x02
+#define VIRTGPU_KUMQUAT_EXECBUF_RING_IDX 0x04
+#define VIRTGPU_KUMQUAT_EXECBUF_SHAREABLE_IN 0x08
+#define VIRTGPU_KUMQUAT_EXECBUF_SHAREABLE_OUT 0x10
+
+#define VIRTGPU_KUMQUAT_EXECBUF_FLAGS \
+ (VIRTGPU_EXECBUF_FENCE_HANDLE_IN | VIRTGPU_EXECBUF_FENCE_HANDLE_OUT | \
+ VIRTGPU_EXECBUF_RING_IDX | VIRTGPU_EXECBUF_SHAREABLE_IN | VIRTGPU_EXECBUF_SHAREABLE_OUT | 0)
+
+/* fence_fd is modified on success if VIRTGPU_KUMQUAT_EXECBUF_FENCE_HANDLE_OUT flag is set. */
+struct drm_kumquat_execbuffer {
+ uint32_t flags;
+ uint32_t size;
+ uint64_t command; /* void* */
+ uint64_t bo_handles;
+ uint32_t num_bo_handles;
+ int64_t fence_handle; /* in/out fence fd (see VIRTGPU_KUMQUAT_EXECBUF_FENCE_HANDLE_IN/OUT) */
+ uint32_t ring_idx; /* command ring index (see VIRTGPU_KUMQUAT_EXECBUF_RING_IDX) */
+ uint32_t syncobj_stride; /* size of @drm_kumquat_execbuffer_syncobj */
+ uint32_t num_in_syncobjs;
+ uint32_t num_out_syncobjs;
+ uint64_t in_syncobjs;
+ uint64_t out_syncobjs;
+};
+
+#define VIRTGPU_KUMQUAT_PARAM_3D_FEATURES 1 /* do we have 3D features in the hw */
+#define VIRTGPU_KUMQUAT_PARAM_CAPSET_QUERY_FIX 2 /* do we have the capset fix */
+#define VIRTGPU_KUMQUAT_PARAM_RESOURCE_BLOB 3 /* DRM_VIRTGPU_RESOURCE_CREATE_BLOB */
+#define VIRTGPU_KUMQUAT_PARAM_HOST_VISIBLE 4 /* Host blob resources are mappable */
+#define VIRTGPU_KUMQUAT_PARAM_CROSS_DEVICE 5 /* Cross virtio-device resource sharing */
+#define VIRTGPU_KUMQUAT_PARAM_CONTEXT_INIT 6 /* DRM_VIRTGPU_KUMQUAT_CONTEXT_INIT */
+#define VIRTGPU_KUMQUAT_PARAM_SUPPORTED_CAPSET_IDs 7 /* Bitmask of supported capability set ids */
+#define VIRTGPU_KUMQUAT_PARAM_EXPLICIT_DEBUG_NAME 8 /* Ability to set debug name from userspace */
+#define VIRTGPU_KUMQUAT_PARAM_FENCE_PASSING 9 /* Host shareable fences */
+#define VIRTGPU_KUMQUAT_PARAM_CREATE_GUEST_HANDLE 10
+
+struct drm_kumquat_getparam {
+ uint64_t param;
+ uint64_t value;
+};
+
+struct drm_kumquat_resource_create_3d {
+ uint32_t target;
+ uint32_t format;
+ uint32_t bind;
+ uint32_t width;
+ uint32_t height;
+ uint32_t depth;
+ uint32_t array_size;
+ uint32_t last_level;
+ uint32_t nr_samples;
+ uint32_t flags;
+ uint32_t bo_handle;
+ uint32_t res_handle;
+ uint32_t size;
+ uint32_t stride;
+};
+
+struct drm_kumquat_vk_device_id {
+ uint8_t device_uuid[16];
+ uint8_t driver_uuid[16];
+};
+
+struct drm_kumquat_vk_info {
+ uint32_t memory_idx;
+ struct drm_kumquat_vk_device_id device_id;
+};
+
+struct drm_kumquat_resource_info {
+ uint32_t bo_handle;
+ struct drm_kumquat_vk_info vulkan_info;
+};
+
+struct drm_kumquat_3d_box {
+ uint32_t x;
+ uint32_t y;
+ uint32_t z;
+ uint32_t w;
+ uint32_t h;
+ uint32_t d;
+};
+
+struct drm_kumquat_transfer_to_host {
+ uint32_t bo_handle;
+ struct drm_kumquat_3d_box box;
+ uint32_t level;
+ uint32_t offset;
+ uint32_t stride;
+ uint32_t layer_stride;
+};
+
+struct drm_kumquat_transfer_from_host {
+ uint32_t bo_handle;
+ struct drm_kumquat_3d_box box;
+ uint32_t level;
+ uint32_t offset;
+ uint32_t stride;
+ uint32_t layer_stride;
+};
+
+struct drm_kumquat_wait {
+ uint32_t handle; /* 0 is an invalid handle */
+ uint32_t flags;
+};
+
+struct drm_kumquat_get_caps {
+ uint32_t cap_set_id;
+ uint32_t cap_set_ver;
+ uint64_t addr;
+ uint32_t size;
+ uint32_t pad;
+};
+
+struct drm_kumquat_resource_create_blob {
+#define VIRTGPU_KUMQUAT_MEM_GUEST 0x0001
+#define VIRTGPU_KUMQUAT_MEM_HOST3D 0x0002
+#define VIRTGPU_KUMQUAT_MEM_HOST3D_GUEST 0x0003
+
+#define VIRTGPU_KUMQUAT_FLAG_USE_MAPPABLE 0x0001
+#define VIRTGPU_KUMQUAT_FLAG_USE_SHAREABLE 0x0002
+#define VIRTGPU_KUMQUAT_FLAG_USE_CROSS_DEVICE 0x0004
+ /* zero is invalid blob_mem */
+ uint32_t blob_mem;
+ uint32_t blob_flags;
+ uint32_t bo_handle;
+ uint32_t res_handle;
+ uint64_t size;
+
+ /*
+ * for 3D contexts with VIRTGPU_KUMQUAT_MEM_HOST3D_GUEST and
+ * VIRTGPU_KUMQUAT_MEM_HOST3D otherwise, must be zero.
+ */
+ uint32_t pad;
+ uint32_t cmd_size;
+ uint64_t cmd;
+ uint64_t blob_id;
+};
+
+struct drm_kumquat_resource_unref {
+ uint32_t bo_handle;
+ uint32_t pad;
+};
+
+#define VIRTGPU_KUMQUAT_CONTEXT_PARAM_CAPSET_ID 0x0001
+#define VIRTGPU_KUMQUAT_CONTEXT_PARAM_NUM_RINGS 0x0002
+#define VIRTGPU_KUMQUAT_CONTEXT_PARAM_POLL_RINGS_MASK 0x0003
+#define VIRTGPU_KUMQUAT_CONTEXT_PARAM_DEBUG_NAME 0x0004
+struct drm_kumquat_context_set_param {
+ uint64_t param;
+ uint64_t value;
+};
+
+struct drm_kumquat_context_init {
+ uint32_t num_params;
+ uint32_t pad;
+
+ /* pointer to drm_kumquat_context_set_param array */
+ uint64_t ctx_set_params;
+};
+
+/*
+ * Without VIRTGPU_KUMQUAT_EMULATED_EXPORT, the server side descriptor will
+ * be provided.
+ *
+ * With VIRTGPU_KUMQUAT_EMULATED_EXPORT, a shared memory descriptor embedded
+ * with resource will be provided.
+ */
+#define VIRTGPU_KUMQUAT_EMULATED_EXPORT 0x0001
+
+#define VIRTGPU_KUMQUAT_HANDLE_TYPE_MEM_OPAQUE_FD 0x1
+#define VIRTGPU_KUMQUAT_HANDLE_TYPE_MEM_DMABUF 0x2
+#define VIRTGPU_KUMQUAT_HANDLE_TYPE_MEM_OPAQUE_WIN32 0x3
+#define VIRTGPU_KUMQUAT_HANDLE_TYPE_MEM_SHM 0x4
+#define VIRTGPU_KUMQUAT_HANDLE_TYPE_MEM_ZIRCON 0x5
+
+#define VIRTGPU_KUMQUAT_HANDLE_TYPE_SIGNAL_OPAQUE_FD 0x10
+#define VIRTGPU_KUMQUAT_HANDLE_TYPE_SIGNAL_SYNC_FD 0x0x20
+#define VIRTGPU_KUMQUAT_HANDLE_TYPE_SIGNAL_OPAQUE_WIN32 0x30
+#define VIRTGPU_KUMQUAT_HANDLE_TYPE_SIGNAL_ZIRCON 0x40
+#define VIRTGPU_KUMQUAT_HANDLE_TYPE_SIGNAL_EVENT_FD 0x50
+
+struct drm_kumquat_resource_export {
+ uint32_t bo_handle;
+ uint32_t flags;
+ int64_t os_handle;
+ uint32_t handle_type;
+};
+
+struct drm_kumquat_resource_import {
+ int64_t os_handle;
+ uint32_t handle_type;
+ uint32_t bo_handle;
+ uint32_t res_handle;
+ uint64_t size;
+};
+
+int32_t virtgpu_kumquat_init(struct virtgpu_kumquat **ptr, const char *gpu_socket);
+
+int32_t virtgpu_kumquat_finish(struct virtgpu_kumquat **ptr);
+
+int32_t virtgpu_kumquat_get_param(struct virtgpu_kumquat *ptr, struct drm_kumquat_getparam *cmd);
+
+int32_t virtgpu_kumquat_get_caps(struct virtgpu_kumquat *ptr, struct drm_kumquat_get_caps *cmd);
+
+int32_t virtgpu_kumquat_context_init(struct virtgpu_kumquat *ptr,
+ struct drm_kumquat_context_init *cmd);
+
+int32_t virtgpu_kumquat_resource_create_3d(struct virtgpu_kumquat *ptr,
+ struct drm_kumquat_resource_create_3d *cmd);
+
+int32_t virtgpu_kumquat_resource_create_blob(struct virtgpu_kumquat *ptr,
+ struct drm_kumquat_resource_create_blob *cmd);
+
+int32_t virtgpu_kumquat_resource_unref(struct virtgpu_kumquat *ptr,
+ struct drm_kumquat_resource_unref *cmd);
+
+int32_t virtgpu_kumquat_resource_map(struct virtgpu_kumquat *ptr, struct drm_kumquat_map *cmd);
+
+int32_t virtgpu_kumquat_resource_unmap(struct virtgpu_kumquat *ptr, uint32_t bo_handle);
+
+int32_t virtgpu_kumquat_transfer_to_host(struct virtgpu_kumquat *ptr,
+ struct drm_kumquat_transfer_to_host *cmd);
+
+int32_t virtgpu_kumquat_transfer_from_host(struct virtgpu_kumquat *ptr,
+ struct drm_kumquat_transfer_from_host *cmd);
+
+int32_t virtgpu_kumquat_execbuffer(struct virtgpu_kumquat *ptr, struct drm_kumquat_execbuffer *cmd);
+
+int32_t virtgpu_kumquat_wait(struct virtgpu_kumquat *ptr, struct drm_kumquat_wait *cmd);
+
+// The following commands are more emulated than the rest.
+int32_t virtgpu_kumquat_resource_export(struct virtgpu_kumquat *ptr,
+ struct drm_kumquat_resource_export *cmd);
+
+int32_t virtgpu_kumquat_resource_import(struct virtgpu_kumquat *ptr,
+ struct drm_kumquat_resource_import *cmd);
+
+int32_t virtgpu_kumquat_resource_info(struct virtgpu_kumquat *ptr,
+ struct drm_kumquat_resource_info *cmd);
+
+int32_t virtgpu_kumquat_snapshot_save(struct virtgpu_kumquat *ptr);
+
+int32_t virtgpu_kumquat_snapshot_restore(struct virtgpu_kumquat *ptr);
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif
diff --git a/src/virtio/virtgpu_kumquat_ffi/lib.rs b/src/virtio/virtgpu_kumquat_ffi/lib.rs
new file mode 100644
index 00000000000..544ee17cf16
--- /dev/null
+++ b/src/virtio/virtgpu_kumquat_ffi/lib.rs
@@ -0,0 +1,424 @@
+// Copyright 2025 Google
+// SPDX-License-Identifier: MIT
+
+use std::ffi::c_char;
+use std::ffi::c_void;
+use std::ffi::CStr;
+use std::panic::catch_unwind;
+use std::panic::AssertUnwindSafe;
+use std::ptr::null_mut;
+use std::slice::from_raw_parts;
+use std::slice::from_raw_parts_mut;
+use std::sync::Mutex;
+
+use libc::EINVAL;
+use libc::ESRCH;
+use log::error;
+use mesa3d_util::FromRawDescriptor;
+use mesa3d_util::IntoRawDescriptor;
+use mesa3d_util::MesaHandle;
+use mesa3d_util::MesaResult;
+use mesa3d_util::OwnedDescriptor;
+use mesa3d_util::RawDescriptor;
+use mesa3d_util::DEFAULT_RAW_DESCRIPTOR;
+use virtgpu_kumquat::defines::*;
+use virtgpu_kumquat::VirtGpuKumquat;
+
+const NO_ERROR: i32 = 0;
+
+fn return_result<T>(result: MesaResult<T>) -> i32 {
+ if let Err(e) = result {
+ error!("An error occurred: {}", e);
+ -EINVAL
+ } else {
+ NO_ERROR
+ }
+}
+
+macro_rules! return_on_error {
+ ($result:expr) => {
+ match $result {
+ Ok(t) => t,
+ Err(e) => {
+ error!("An error occurred: {}", e);
+ return -EINVAL;
+ }
+ }
+ };
+}
+
+#[allow(non_camel_case_types)]
+type virtgpu_kumquat_ffi = Mutex<VirtGpuKumquat>;
+
+// The following structs (in define.rs) must be ABI-compatible with FFI header
+// (virtgpu_kumquat_ffi.h).
+
+#[allow(non_camel_case_types)]
+type drm_kumquat_getparam = VirtGpuParam;
+
+#[allow(non_camel_case_types)]
+type drm_kumquat_resource_unref = VirtGpuResourceUnref;
+
+#[allow(non_camel_case_types)]
+type drm_kumquat_get_caps = VirtGpuGetCaps;
+
+#[allow(non_camel_case_types)]
+type drm_kumquat_context_init = VirtGpuContextInit;
+
+#[allow(non_camel_case_types)]
+type drm_kumquat_resource_create_3d = VirtGpuResourceCreate3D;
+
+#[allow(non_camel_case_types)]
+type drm_kumquat_resource_create_blob = VirtGpuResourceCreateBlob;
+
+#[allow(non_camel_case_types)]
+type drm_kumquat_transfer_to_host = VirtGpuTransfer;
+
+#[allow(non_camel_case_types)]
+type drm_kumquat_transfer_from_host = VirtGpuTransfer;
+
+#[allow(non_camel_case_types)]
+type drm_kumquat_execbuffer = VirtGpuExecBuffer;
+
+#[allow(non_camel_case_types)]
+type drm_kumquat_wait = VirtGpuWait;
+
+#[allow(non_camel_case_types)]
+type drm_kumquat_resource_map = VirtGpuResourceMap;
+
+#[allow(non_camel_case_types)]
+type drm_kumquat_resource_export = VirtGpuResourceExport;
+
+#[allow(non_camel_case_types)]
+type drm_kumquat_resource_import = VirtGpuResourceImport;
+
+#[allow(non_camel_case_types)]
+type drm_kumquat_resource_info = VirtGpuResourceInfo;
+
+#[no_mangle]
+pub unsafe extern "C" fn virtgpu_kumquat_init(
+ ptr: &mut *mut virtgpu_kumquat_ffi,
+ gpu_socket: Option<&c_char>,
+) -> i32 {
+ catch_unwind(AssertUnwindSafe(|| {
+ let gpu_socket_str = match gpu_socket {
+ Some(value) => {
+ // SAFETY:
+ // The API user must pass in a valid C-string.
+ let c_str_slice = unsafe { CStr::from_ptr(value) };
+ let result = c_str_slice.to_str();
+ return_on_error!(result)
+ }
+ None => "/tmp/kumquat-gpu-0",
+ };
+
+ let result = VirtGpuKumquat::new(gpu_socket_str);
+ let kmqt = return_on_error!(result);
+ *ptr = Box::into_raw(Box::new(Mutex::new(kmqt))) as _;
+ NO_ERROR
+ }))
+ .unwrap_or(-ESRCH)
+}
+
+#[no_mangle]
+pub extern "C" fn virtgpu_kumquat_finish(ptr: &mut *mut virtgpu_kumquat_ffi) -> i32 {
+ catch_unwind(AssertUnwindSafe(|| {
+ let _ = unsafe { Box::from_raw(*ptr) };
+ *ptr = null_mut();
+ NO_ERROR
+ }))
+ .unwrap_or(-ESRCH)
+}
+
+#[no_mangle]
+pub unsafe extern "C" fn virtgpu_kumquat_get_param(
+ ptr: &mut virtgpu_kumquat_ffi,
+ cmd: &mut drm_kumquat_getparam,
+) -> i32 {
+ catch_unwind(AssertUnwindSafe(|| {
+ let result = ptr.lock().unwrap().get_param(cmd);
+ return_result(result)
+ }))
+ .unwrap_or(-ESRCH)
+}
+
+#[no_mangle]
+pub unsafe extern "C" fn virtgpu_kumquat_get_caps(
+ ptr: &mut virtgpu_kumquat_ffi,
+ cmd: &drm_kumquat_get_caps,
+) -> i32 {
+ catch_unwind(AssertUnwindSafe(|| {
+ let caps_slice = if cmd.size != 0 {
+ // SAFETY:
+ // The API user must pass in a valid array to hold capset data.
+ unsafe { from_raw_parts_mut(cmd.addr as *mut u8, cmd.size as usize) }
+ } else {
+ &mut []
+ };
+ let result = ptr.lock().unwrap().get_caps(cmd.cap_set_id, caps_slice);
+ return_result(result)
+ }))
+ .unwrap_or(-ESRCH)
+}
+
+#[no_mangle]
+pub unsafe extern "C" fn virtgpu_kumquat_context_init(
+ ptr: &mut virtgpu_kumquat_ffi,
+ cmd: &drm_kumquat_context_init,
+) -> i32 {
+ catch_unwind(AssertUnwindSafe(|| {
+ let context_params: &[VirtGpuParam] = if cmd.num_params != 0 {
+ // SAFETY:
+ // The API user must pass in a valid array of context parameters.
+ unsafe {
+ from_raw_parts(
+ cmd.ctx_set_params as *const VirtGpuParam,
+ cmd.num_params as usize,
+ )
+ }
+ } else {
+ &[]
+ };
+
+ let mut capset_id: u64 = 0;
+
+ for param in context_params {
+ match param.param {
+ VIRTGPU_KUMQUAT_CONTEXT_PARAM_CAPSET_ID => {
+ capset_id = param.value;
+ }
+ _ => (),
+ }
+ }
+
+ let result = ptr.lock().unwrap().context_create(capset_id, "");
+ return_result(result)
+ }))
+ .unwrap_or(-ESRCH)
+}
+
+#[no_mangle]
+pub unsafe extern "C" fn virtgpu_kumquat_resource_create_3d(
+ ptr: &mut virtgpu_kumquat_ffi,
+ cmd: &mut drm_kumquat_resource_create_3d,
+) -> i32 {
+ catch_unwind(AssertUnwindSafe(|| {
+ let result = ptr.lock().unwrap().resource_create_3d(cmd);
+ return_result(result)
+ }))
+ .unwrap_or(-ESRCH)
+}
+
+#[no_mangle]
+pub unsafe extern "C" fn virtgpu_kumquat_resource_create_blob(
+ ptr: &mut virtgpu_kumquat_ffi,
+ cmd: &mut drm_kumquat_resource_create_blob,
+) -> i32 {
+ catch_unwind(AssertUnwindSafe(|| {
+ let blob_cmd = if cmd.cmd_size != 0 {
+ // SAFETY:
+ // The API user must pass in a valid command buffer with correct size.
+ unsafe { from_raw_parts(cmd.cmd as *const u8, cmd.cmd_size as usize) }
+ } else {
+ &[]
+ };
+ let result = ptr.lock().unwrap().resource_create_blob(cmd, blob_cmd);
+ return_result(result)
+ }))
+ .unwrap_or(-ESRCH)
+}
+
+#[no_mangle]
+pub unsafe extern "C" fn virtgpu_kumquat_resource_unref(
+ ptr: &mut virtgpu_kumquat_ffi,
+ cmd: &mut drm_kumquat_resource_unref,
+) -> i32 {
+ catch_unwind(AssertUnwindSafe(|| {
+ let result = ptr.lock().unwrap().resource_unref(cmd.bo_handle);
+ return_result(result)
+ }))
+ .unwrap_or(-ESRCH)
+}
+
+#[no_mangle]
+pub unsafe extern "C" fn virtgpu_kumquat_resource_map(
+ ptr: &mut virtgpu_kumquat_ffi,
+ cmd: &mut drm_kumquat_resource_map,
+) -> i32 {
+ catch_unwind(AssertUnwindSafe(|| {
+ let result = ptr.lock().unwrap().map(cmd.bo_handle);
+ let internal_map = return_on_error!(result);
+ (*cmd).ptr = internal_map.ptr as *mut c_void;
+ (*cmd).size = internal_map.size;
+ NO_ERROR
+ }))
+ .unwrap_or(-ESRCH)
+}
+
+#[no_mangle]
+pub unsafe extern "C" fn virtgpu_kumquat_resource_unmap(
+ ptr: &mut virtgpu_kumquat_ffi,
+ bo_handle: u32,
+) -> i32 {
+ catch_unwind(AssertUnwindSafe(|| {
+ let result = ptr.lock().unwrap().unmap(bo_handle);
+ return_result(result)
+ }))
+ .unwrap_or(-ESRCH)
+}
+
+#[no_mangle]
+pub unsafe extern "C" fn virtgpu_kumquat_transfer_to_host(
+ ptr: &mut virtgpu_kumquat_ffi,
+ cmd: &mut drm_kumquat_transfer_to_host,
+) -> i32 {
+ catch_unwind(AssertUnwindSafe(|| {
+ let result = ptr.lock().unwrap().transfer_to_host(cmd);
+ return_result(result)
+ }))
+ .unwrap_or(-ESRCH)
+}
+
+#[no_mangle]
+pub unsafe extern "C" fn virtgpu_kumquat_transfer_from_host(
+ ptr: &mut virtgpu_kumquat_ffi,
+ cmd: &mut drm_kumquat_transfer_from_host,
+) -> i32 {
+ catch_unwind(AssertUnwindSafe(|| {
+ let result = ptr.lock().unwrap().transfer_from_host(cmd);
+ return_result(result)
+ }))
+ .unwrap_or(-ESRCH)
+}
+
+#[no_mangle]
+pub unsafe extern "C" fn virtgpu_kumquat_execbuffer(
+ ptr: &mut virtgpu_kumquat_ffi,
+ cmd: &mut drm_kumquat_execbuffer,
+) -> i32 {
+ catch_unwind(AssertUnwindSafe(|| {
+ let bo_handles = if cmd.num_bo_handles != 0 {
+ // SAFETY:
+ // The API user must pass in a valid array of bo_handles with correct size.
+ unsafe { from_raw_parts(cmd.bo_handles as *const u32, cmd.num_bo_handles as usize) }
+ } else {
+ &[]
+ };
+
+ let cmd_buf = if cmd.size != 0 {
+ // SAFETY:
+ // The API user must pass in a valid command buffer with correct size.
+ unsafe { from_raw_parts(cmd.command as *const u8, cmd.size as usize) }
+ } else {
+ &[]
+ };
+
+ // TODO
+ let in_fences: &[u64] = &[0; 0];
+
+ let mut descriptor: RawDescriptor = DEFAULT_RAW_DESCRIPTOR;
+ let result = ptr.lock().unwrap().submit_command(
+ cmd.flags,
+ bo_handles,
+ cmd_buf,
+ cmd.ring_idx,
+ in_fences,
+ &mut descriptor,
+ );
+
+ cmd.fence_handle = descriptor as i64;
+ return_result(result)
+ }))
+ .unwrap_or(-ESRCH)
+}
+
+#[no_mangle]
+pub unsafe extern "C" fn virtgpu_kumquat_wait(
+ ptr: &mut virtgpu_kumquat_ffi,
+ cmd: &mut drm_kumquat_wait,
+) -> i32 {
+ catch_unwind(AssertUnwindSafe(|| {
+ let result = ptr.lock().unwrap().wait(cmd.bo_handle);
+ return_result(result)
+ }))
+ .unwrap_or(-ESRCH)
+}
+
+#[no_mangle]
+pub extern "C" fn virtgpu_kumquat_resource_export(
+ ptr: &mut virtgpu_kumquat_ffi,
+ cmd: &mut drm_kumquat_resource_export,
+) -> i32 {
+ catch_unwind(AssertUnwindSafe(|| {
+ let result = ptr
+ .lock()
+ .unwrap()
+ .resource_export(cmd.bo_handle, cmd.flags);
+ let hnd = return_on_error!(result);
+
+ (*cmd).handle_type = hnd.handle_type;
+ (*cmd).os_handle = hnd.os_handle.into_raw_descriptor() as i64;
+ NO_ERROR
+ }))
+ .unwrap_or(-ESRCH)
+}
+
+#[no_mangle]
+pub unsafe extern "C" fn virtgpu_kumquat_resource_import(
+ ptr: &mut virtgpu_kumquat_ffi,
+ cmd: &mut drm_kumquat_resource_import,
+) -> i32 {
+ catch_unwind(AssertUnwindSafe(|| {
+ let handle = MesaHandle {
+ // SAFETY:
+ // The API user must transfer ownership of a valid OS handle.
+ os_handle: unsafe {
+ OwnedDescriptor::from_raw_descriptor((*cmd).os_handle.into_raw_descriptor())
+ },
+ handle_type: (*cmd).handle_type,
+ };
+
+ let result = ptr.lock().unwrap().resource_import(
+ handle,
+ &mut cmd.bo_handle,
+ &mut cmd.res_handle,
+ &mut cmd.size,
+ );
+
+ return_result(result)
+ }))
+ .unwrap_or(-ESRCH)
+}
+
+#[no_mangle]
+pub extern "C" fn virtgpu_kumquat_resource_info(
+ ptr: &mut virtgpu_kumquat_ffi,
+ cmd: &mut drm_kumquat_resource_info,
+) -> i32 {
+ catch_unwind(AssertUnwindSafe(|| {
+ let result = ptr.lock().unwrap().resource_info(cmd.bo_handle);
+
+ let info = return_on_error!(result);
+ (*cmd).vulkan_info = info;
+ NO_ERROR
+ }))
+ .unwrap_or(-ESRCH)
+}
+
+#[no_mangle]
+pub unsafe extern "C" fn virtgpu_kumquat_snapshot_save(ptr: &mut virtgpu_kumquat_ffi) -> i32 {
+ catch_unwind(AssertUnwindSafe(|| {
+ let result = ptr.lock().unwrap().snapshot();
+ return_result(result)
+ }))
+ .unwrap_or(-ESRCH)
+}
+
+#[no_mangle]
+pub unsafe extern "C" fn virtgpu_kumquat_snapshot_restore(ptr: &mut virtgpu_kumquat_ffi) -> i32 {
+ catch_unwind(AssertUnwindSafe(|| {
+ let result = ptr.lock().unwrap().restore();
+ return_result(result)
+ }))
+ .unwrap_or(-ESRCH)
+}
diff --git a/src/virtio/virtgpu_kumquat_ffi/meson.build b/src/virtio/virtgpu_kumquat_ffi/meson.build
new file mode 100644
index 00000000000..ca30eb5ecd5
--- /dev/null
+++ b/src/virtio/virtgpu_kumquat_ffi/meson.build
@@ -0,0 +1,27 @@
+# Copyright © 2025 Google
+# SPDX-License-Identifier: MIT
+
+inc_virtgpu_kumquat_ffi = include_directories('include')
+
+dep_log = dependency('log',
+ version: '>= 0.4.22',
+ fallback: ['log', 'dep_log'],
+ required: true,
+)
+
+virtgpu_kumquat_ffi_args = [
+ # we want unsafe blocks inside unsafe functions
+ '-Dunsafe_op_in_unsafe_fn',
+]
+
+libvirtgpu_kumquat_ffi = static_library(
+ 'virtgpu_kumquat_ffi',
+ 'lib.rs',
+ gnu_symbol_visibility : 'hidden',
+ rust_abi : 'c',
+ rust_args : [
+ virtgpu_kumquat_ffi_args,
+ ],
+ link_with: [libmesa_protocols, libmesa_rust_util, libvirtgpu_kumquat],
+ dependencies: [dep_mesa3d_util, dep_log]
+)