Merge branch 'drm4' into 'drm'

Introduce infrastructure for serializing ioctls

See merge request redox-os/relibc!806
This commit is contained in:
Jeremy Soller
2025-12-15 14:54:48 -07:00
3 changed files with 254 additions and 79 deletions
+34 -69
View File
@@ -12,7 +12,7 @@ use crate::{
platform::types::*,
};
use super::{IoctlBuffer, graphics_ipc as ipc};
use super::{IoctlBuffer, graphics_ipc as ipc, graphics_ipc::IoctlData};
const DRM_FORMAT_ARGB8888: u32 = 0x34325241; // 'AR24' fourcc code, for ARGB8888
@@ -70,39 +70,38 @@ impl Dev {
)
}
fn version(&self) -> Result<ipc::Version> {
let mut cmd = ipc::Version {
version_major: 0,
version_minor: 0,
version_patchlevel: 0,
name_len: 0,
name: [0; 16],
desc_len: 0,
desc: [0; 16],
};
unsafe {
self.call(&mut cmd, ipc::VERSION)?;
}
Ok(cmd)
unsafe fn read_write_ioctl<T: ipc::IoctlData>(
&self,
mut buf: IoctlBuffer,
func: u64,
) -> Result<c_int> {
let mut data = buf.read::<T>()?;
let mut wire = data.write();
let res = redox_rt::sys::sys_call(
self.fd as usize,
&mut wire,
syscall::CallFlags::empty(),
&[func],
)?;
data.read_from(&wire);
buf.write(data)?;
Ok(res as c_int)
}
fn get_cap(&self, capability: u64) -> Result<u64> {
let mut cmd = drm_get_cap {
capability,
value: 0,
};
unsafe {
self.call(&mut cmd, ipc::GET_CAP)?;
}
Ok(cmd.value)
}
fn set_client_cap(&self, capability: u64, value: u64) -> Result<()> {
let mut cmd = drm_set_client_cap { capability, value };
unsafe {
self.call(&mut cmd, ipc::SET_CLIENT_CAP)?;
}
Ok(())
unsafe fn write_ioctl<T: ipc::IoctlData>(
&self,
mut buf: IoctlBuffer,
func: u64,
) -> Result<c_int> {
let mut data = buf.read::<T>()?;
let mut wire = data.write();
let res = redox_rt::sys::sys_call(
self.fd as usize,
&mut wire,
syscall::CallFlags::empty(),
&[func],
)?;
Ok(res as c_int)
}
fn display_count(&self) -> Result<usize> {
@@ -126,40 +125,6 @@ impl Dev {
}
}
unsafe fn version(dev: Dev, mut buf: IoctlBuffer) -> Result<c_int> {
let mut vers = buf.read::<drm_version>()?;
let version = dev.version()?;
vers.version_major = version.version_major as i32;
vers.version_minor = version.version_minor as i32;
vers.version_patchlevel = version.version_patchlevel as i32;
vers.name_len = copy_array(
&version.name[..version.name_len],
vers.name as *mut u8,
vers.name_len,
);
vers.date_len = copy_array("0".as_bytes(), vers.date as *mut u8, vers.date_len);
vers.desc_len = copy_array(
&version.desc[..version.desc_len],
vers.desc as *mut u8,
vers.desc_len,
);
buf.write(vers)?;
Ok(0)
}
unsafe fn get_cap(dev: Dev, mut buf: IoctlBuffer) -> Result<c_int> {
let mut cap = buf.read::<drm_get_cap>()?;
cap.value = dev.get_cap(cap.capability)?;
buf.write(cap)?;
Ok(0)
}
unsafe fn set_client_cap(dev: Dev, mut buf: IoctlBuffer) -> Result<c_int> {
let mut cap = buf.read::<drm_set_client_cap>()?;
dev.set_client_cap(cap.capability, cap.value)?;
Ok(0)
}
unsafe fn mode_card_res(dev: Dev, mut buf: IoctlBuffer) -> Result<c_int> {
let mut res = buf.read::<drm_mode_card_res>()?;
let count = dev.display_count()?;
@@ -311,9 +276,9 @@ unsafe fn mode_get_fb2(dev: Dev, mut buf: IoctlBuffer) -> Result<c_int> {
pub(super) unsafe fn ioctl(fd: c_int, func: u8, buf: IoctlBuffer) -> Result<c_int> {
let dev = Dev::new(fd)?;
match func {
0x00 => version(dev, buf),
0x0C => get_cap(dev, buf),
0x0D => set_client_cap(dev, buf),
0x00 => dev.read_write_ioctl::<drm_version>(buf, ipc::VERSION),
0x0C => dev.read_write_ioctl::<drm_get_cap>(buf, ipc::GET_CAP),
0x0D => dev.write_ioctl::<drm_set_client_cap>(buf, ipc::SET_CLIENT_CAP),
0xA0 => mode_card_res(dev, buf),
0xA1 => mode_get_crtc(dev, buf),
0xA6 => mode_get_encoder(dev, buf),
+219 -10
View File
@@ -8,24 +8,233 @@ pub struct Damage {
pub height: u32,
}
use alloc::vec::Vec;
use core::{
cmp,
ffi::{c_char, c_int},
iter, mem, slice,
};
pub trait IoctlData {
unsafe fn write(&self) -> Vec<u8>;
unsafe fn read_from(&mut self, buf: &[u8]);
}
macro_rules! define_ioctl_data {
(struct $ioctl_ty:ident, $mem_ty:ident {
$($rest:tt)*
}) => {
define_ioctl_data!(
struct $ioctl_ty, $mem_ty { $($rest)* } => (), (), ()
);
};
(struct $ioctl_ty:ident, $mem_ty:ident {
$field:ident: $ty:ty,
$($rest:tt)*
} =>
($($ioctl_fields:tt)*),
($($counted_fields:tt)*),
($($noncounted_fields:tt)*)
) => {
define_ioctl_data!(
struct $ioctl_ty, $mem_ty { $($rest)* } =>
($($ioctl_fields)* $field: $ty,),
($($counted_fields)*),
($($noncounted_fields)* $field: $ty,)
);
};
(struct $ioctl_ty:ident, $mem_ty:ident {
$field:ident: $ty:ty [array<$el:ident, $counted_by:ident>],
$($rest:tt)*
} =>
($($ioctl_fields:tt)*),
($($counted_fields:tt)*),
($($noncounted_fields:tt)*)
) => {
define_ioctl_data!(
struct $ioctl_ty, $mem_ty { $($rest)* } =>
($($ioctl_fields)* $field: $ty,),
($($counted_fields)* $field: $ty [array<$el, $counted_by>],),
($($noncounted_fields)*)
);
};
(struct $ioctl_ty:ident, $mem_ty:ident {} =>
($($ioctl_field:ident: $ioctl_field_ty:ty,)*),
($($counted_field:ident: $counted_ty:ty [array<$el:ident, $counted_by:ident>],)*),
($($noncounted_field:ident: $noncounted_ty:ty,)*)
) => {
pub use drm_sys::$ioctl_ty;
// FIXME check ioctl_ty doesn't have padding
const _: $ioctl_ty = $ioctl_ty {
$($ioctl_field: unsafe { mem::zeroed::<$ioctl_field_ty>() },)*
};
#[repr(C)]
pub struct ${concat(__, $mem_ty, Noncounted)} {
$($noncounted_field: $noncounted_ty,)*
}
pub struct $mem_ty<'a> {
noncounted_fields: &'a mut ${concat(__, $mem_ty, Noncounted)},
$($counted_field: &'a mut [$el],)*
}
impl IoctlData for $ioctl_ty {
unsafe fn write(&self) -> Vec<u8> {
let noncounted_fields = ${concat(__, $mem_ty, Noncounted)} {
$($noncounted_field: self.$noncounted_field,)*
};
// FIXME use Vec::with_capacity
let mut data = Vec::<u8>::new();
data.extend_from_slice(&unsafe {
mem::transmute::<
${concat(__, $mem_ty, Noncounted)},
[u8; size_of::<${concat(__, $mem_ty, Noncounted)}>()],
>(noncounted_fields)
});
$(
let size = self.$counted_by as usize * size_of::<$el>();
if self.$counted_field as usize != 0 {
let $counted_field = unsafe {
slice::from_raw_parts(self.$counted_field as *const u8, size)
};
data.extend_from_slice(&$counted_field);
} else {
data.extend(iter::repeat(0u8).take(size));
};
)*
data
}
unsafe fn read_from(&mut self, mut buf: &[u8]) {
// FIXME be robust against malicious scheme implementations by returning an error
// when the buf is the wrong size
let noncounted_fields = buf.split_off(..size_of::<${concat(__, $mem_ty, Noncounted)}>()).unwrap();
$(
let size = self.$counted_by as usize * size_of::<$el>();
let $counted_field = buf.split_off(..size).unwrap();
if self.$counted_field as usize != 0 {
unsafe {
slice::from_raw_parts_mut(self.$counted_field as *mut u8, size).copy_from_slice($counted_field);
}
}
)*
assert!(buf.is_empty());
let noncounted_fields = unsafe { &*(noncounted_fields as *const _ as *const ${concat(__, $mem_ty, Noncounted)}) };
$(self.$noncounted_field = noncounted_fields.$noncounted_field;)*
}
}
impl<'a> $mem_ty<'a> {
pub fn with(
mut buf: &'a mut [u8],
f: impl FnOnce($mem_ty<'a>) -> syscall::Result<usize>,
) -> syscall::Result<usize> {
let noncounted_fields = buf.split_off_mut(..size_of::<${concat(__, $mem_ty, Noncounted)}>())
.ok_or(syscall::Error::new(syscall::EINVAL))?;
let noncounted_fields = unsafe { &mut *(noncounted_fields as *mut _ as *mut ${concat(__, $mem_ty, Noncounted)}) };
$(
let $counted_field = buf.split_off_mut(..noncounted_fields.$counted_by as usize * size_of::<$el>())
.ok_or(syscall::Error::new(syscall::EINVAL))?;
let $counted_field = unsafe {
slice::from_raw_parts_mut($counted_field as *mut _ as *mut $el, noncounted_fields.$counted_by as usize)
};
)*
if !buf.is_empty() {
return Err(syscall::Error::new(syscall::EINVAL));
}
Ok( f($mem_ty {
noncounted_fields,
$($counted_field,)*
})?)
}
$(
pub fn $noncounted_field(&self) -> $noncounted_ty {
self.noncounted_fields.$noncounted_field
}
/// Should not be called for fields used as array length
pub fn ${concat(set_, $noncounted_field)}(&mut self, data: $noncounted_ty) {
self.noncounted_fields.$noncounted_field = data;
}
)*
$(
pub fn $counted_field(&self) -> &[$el] {
self.$counted_field
}
pub fn ${concat(set_, $counted_field)}(&mut self, data: &[$el]) {
let copied_count = cmp::min(data.len(), self.$counted_field.len());
self.$counted_field[..copied_count].copy_from_slice(&data[..copied_count]);
self.noncounted_fields.$counted_by = data.len() as _;
}
)*
}
};
}
pub const VERSION: u64 = 0;
#[repr(C, packed)]
pub struct Version {
pub version_major: u32,
pub version_minor: u32,
pub version_patchlevel: u32,
// FIXME allow variable sized fields
pub name_len: usize,
pub name: [u8; 16],
pub desc_len: usize,
pub desc: [u8; 16],
define_ioctl_data! {
struct drm_version, DrmVersion {
version_major: c_int,
version_minor: c_int,
version_patchlevel: c_int,
name_len: drm_sys::__kernel_size_t,
name: *mut c_char [array<c_char, name_len>],
date_len: drm_sys::__kernel_size_t,
date: *mut c_char [array<c_char, date_len>],
desc_len: drm_sys::__kernel_size_t,
desc: *mut c_char [array<c_char, desc_len>],
}
}
pub const GET_CAP: u64 = 0x0C;
pub use drm_sys::DRM_CAP_DUMB_BUFFER;
define_ioctl_data! {
struct drm_get_cap, DrmGetCap {
capability: u64,
value: u64,
}
}
pub const SET_CLIENT_CAP: u64 = 0x0D;
pub use drm_sys::DRM_CLIENT_CAP_CURSOR_PLANE_HOTSPOT;
define_ioctl_data! {
struct drm_set_client_cap, DrmSetClientCap {
capability: u64,
value: u64,
}
}
pub const MODE_CARD_RES: u64 = 0xA0;
define_ioctl_data! {
struct drm_mode_card_res, DrmModeCardRes {
fb_id_ptr: u64 [array<u32, count_fbs>],
crtc_id_ptr: u64 [array<u32, count_crtcs>],
connector_id_ptr: u64 [array<u32, count_connectors>],
encoder_id_ptr: u64 [array<u32, count_encoders>],
count_fbs: u32,
count_crtcs: u32,
count_connectors: u32,
count_encoders: u32,
min_width: u32,
max_width: u32,
min_height: u32,
max_height: u32,
}
}
// FIXME replace these with proper drm interfaces
pub const DISPLAY_COUNT: u64 = 1;
+1
View File
@@ -17,6 +17,7 @@
#![feature(c_variadic)]
#![feature(core_intrinsics)]
#![feature(macro_derive)]
#![feature(macro_metavar_expr_concat)]
#![feature(maybe_uninit_slice)]
#![feature(lang_items)]
#![feature(linkage)]