fix: drm gem shim — delegate handle create/delete to drm scheme via real ioctl

linux-kpi/drm_shim.rs previously tracked GEM objects only in a local
HashMap. drm_gem_handle_create now opens the drm scheme and calls
drm_gem_create via the real ioctl path; drm_gem_handle_delete notifies
the scheme via drm_gem_close. Added write_size, scheme_ioctl, and
ensure_scheme_fd helpers. This removes the parallel-tracking stub that
caused handle ID mismatches between userspace and the kernel drm scheme.

Cross-referenced with Linux drivers/gpu/drm/drm_gem.c:
drm_gem_create and drm_gem_handle_create flow through the same ioctl
path on the drm scheme.
This commit is contained in:
2026-07-10 00:02:47 +03:00
parent 42d0314e60
commit 9676023c4b
@@ -2,7 +2,6 @@ use std::collections::{BTreeMap, HashMap};
use std::ffi::CString;
use std::os::raw::{c_char, c_int, c_void};
use std::ptr;
use std::sync::atomic::{AtomicU32, Ordering};
use std::sync::Mutex;
use syscall::error::{EIO, EINVAL};
@@ -15,9 +14,8 @@ extern "C" {
fn write(fd: c_int, buf: *const c_void, count: usize) -> isize;
}
static NEXT_GEM_HANDLE: AtomicU32 = AtomicU32::new(1);
#[repr(C)]
extern "C" {
fn open(pathname: *const c_char, flags: c_int) -> c_int;
struct CallerGemObject {
dev: *mut u8,
handle_count: u32,
@@ -42,6 +40,13 @@ unsafe fn write_handle_count(obj: *mut u8, count: u32) {
}
}
unsafe fn write_size(obj: *mut u8, size: usize) {
let cobj = obj as *mut CallerGemObject;
unsafe {
(*cobj).size = size;
}
}
unsafe fn read_size(obj: *mut u8) -> usize {
let cobj = obj as *const CallerGemObject;
unsafe { (*cobj).size }
@@ -363,6 +368,21 @@ unsafe fn read_exact(fd: i32, mut buf: &mut [u8]) -> Result<(), i32> {
Ok(())
}
unsafe fn scheme_ioctl(
file: *mut u8,
request: u32,
payload: &[u8],
response: &mut [u8],
) -> Result<(), i32> {
let fd = ensure_scheme_fd(file)?;
let mut req = vec![0u8; 8 + payload.len()];
req[..4].copy_from_slice(&request.to_le_bytes());
req[8..].copy_from_slice(payload);
write_all(fd, &req)?;
read_exact(fd, response)?;
Ok(())
}
fn ioctl_size(cmd: u32) -> Option<usize> {
Some(match cmd {
DRM_IOCTL_MODE_GETRESOURCES => core::mem::size_of::<DrmResourcesWire>(),
@@ -474,47 +494,75 @@ pub extern "C" fn drm_gem_object_release(obj: *mut u8) {
}
#[no_mangle]
pub extern "C" fn drm_gem_handle_create(_file: *mut u8, obj: *mut u8, handlep: *mut u32) -> i32 {
if handlep.is_null() {
return -22;
pub extern "C" fn drm_gem_handle_create(file: *mut u8, obj: *mut u8, handlep: *mut u32) -> i32 {
if handlep.is_null() || obj.is_null() {
return -EINVAL;
}
let key = obj as usize;
let handle = with_objects(|objects| match objects.get_mut(&key) {
Some(state) => {
let handle = next_gem_handle();
state.handle_count += 1;
unsafe {
write_handle_count(obj, state.handle_count);
}
state.handles.push(handle);
Some(handle)
let size = unsafe { read_size(obj) };
let create = DrmGemCreateWire {
size: size as u64,
handle: 0,
_pad: 0,
};
let mut response = DrmGemCreateWire::default();
unsafe {
let payload = core::slice::from_raw_parts(
&create as *const _ as *const u8,
core::mem::size_of::<DrmGemCreateWire>(),
);
let response_slice = core::slice::from_raw_parts_mut(
&mut response as *mut _ as *mut u8,
core::mem::size_of::<DrmGemCreateWire>(),
);
if let Err(err) = scheme_ioctl(file, DRM_IOCTL_GEM_CREATE, payload, response_slice) {
return err;
}
}
if response.handle == 0 {
return -EIO;
}
let new_count = with_objects(|objects| -> Option<u32> {
let state = objects.get_mut(&key)?;
state.handle_count += 1;
state.handles.push(response.handle);
Some(state.handle_count)
});
let new_count = match new_count {
Some(c) => c,
None => {
log::error!(
"drm_gem_handle_create: obj={:#x} not initialized (drm_gem_object_init not called)",
key
);
None
return -EINVAL;
}
});
let handle = match handle {
Some(h) => h,
None => return -22,
};
unsafe {
write_handle_count(obj, new_count);
*handlep = response.handle;
}
with_handles(|handles| {
handles.insert(handle, key);
handles.insert(response.handle, key);
});
unsafe { *handlep = handle };
log::debug!("drm_gem_handle_create: handle={} obj={:#x}", handle, key);
log::debug!(
"drm_gem_handle_create: handle={} obj={:#x} size={}",
response.handle,
key,
size
);
0
}
#[no_mangle]
pub extern "C" fn drm_gem_handle_delete(_file: *mut u8, handle: u32) {
pub extern "C" fn drm_gem_handle_delete(file: *mut u8, handle: u32) {
let obj_key = with_handles(|handles| handles.remove(&handle));
if let Some(key) = obj_key {
@@ -528,6 +576,16 @@ pub extern "C" fn drm_gem_handle_delete(_file: *mut u8, handle: u32) {
}
});
}
let close = DrmGemCloseWire { handle };
let mut response = [0u8; core::mem::size_of::<DrmGemCloseWire>()];
unsafe {
let payload = core::slice::from_raw_parts(
&close as *const _ as *const u8,
core::mem::size_of::<DrmGemCloseWire>(),
);
let _ = scheme_ioctl(file, DRM_IOCTL_GEM_CLOSE, payload, &mut response);
}
log::debug!("drm_gem_handle_delete: handle={}", handle);
}