From c3f4711403eb83fbee9420dca8eaafa03becd65b Mon Sep 17 00:00:00 2001 From: bjorn3 <17426603+bjorn3@users.noreply.github.com> Date: Mon, 15 Dec 2025 21:36:40 +0100 Subject: [PATCH] Introduce infrastructure for serializing ioctls And use it for a couple of drm interfaces --- Cargo.lock | 1 + drivers/graphics/driver-graphics/src/lib.rs | 82 +++---- drivers/graphics/graphics-ipc/Cargo.toml | 1 + drivers/graphics/graphics-ipc/src/lib.rs | 2 + drivers/graphics/graphics-ipc/src/v2.rs | 228 +++++++++++++++++++- 5 files changed, 247 insertions(+), 67 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 8c81318326..d5ff055f3b 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -869,6 +869,7 @@ dependencies = [ "drm-sys", "libredox", "log", + "redox_syscall", ] [[package]] diff --git a/drivers/graphics/driver-graphics/src/lib.rs b/drivers/graphics/driver-graphics/src/lib.rs index 32563c8ea9..db6f2669ef 100644 --- a/drivers/graphics/driver-graphics/src/lib.rs +++ b/drivers/graphics/driver-graphics/src/lib.rs @@ -1,9 +1,9 @@ #![feature(slice_as_array)] -use std::cmp; use std::collections::{BTreeMap, HashMap}; use std::fs::File; use std::io::{self, Write}; +use std::mem; use std::mem::transmute; use std::sync::Arc; @@ -437,68 +437,36 @@ impl SchemeSync for GraphicsScheme { return Err(Error::new(EOPNOTSUPP)); } Handle::V2 { vt, next_id, fbs } => match metadata[0] { - ipc::VERSION => { - if payload.len() < size_of::() { - return Err(Error::new(EINVAL)); - } - let payload = unsafe { - transmute::<&mut [u8; size_of::()], &mut ipc::Version>( - payload.as_mut_array().unwrap(), - ) - }; - payload.version_major = 1; - payload.version_minor = 4; - payload.version_patchlevel = 0; + ipc::VERSION => ipc::DrmVersion::with(payload, |mut data| { + data.set_version_major(1); + data.set_version_minor(4); + data.set_version_patchlevel(0); - let name = self.adapter.name(); - let name_len = cmp::min(name.len(), payload.name_len); - payload.name[..name_len].copy_from_slice(&name[..name_len]); - payload.name_len = name.len(); + data.set_name(unsafe { mem::transmute(self.adapter.name()) }); + data.set_date(unsafe { mem::transmute(&b"0"[..]) }); + data.set_desc(unsafe { mem::transmute(self.adapter.desc()) }); - let desc = self.adapter.desc(); - let desc_len = cmp::min(desc.len(), payload.name_len); - payload.desc[..desc_len].copy_from_slice(&desc[..desc_len]); - payload.desc_len = desc.len(); - - Ok(size_of::()) - } - ipc::GET_CAP => { - if payload.len() < size_of::() { - return Err(Error::new(EINVAL)); - } - let payload = unsafe { - transmute::< - &mut [u8; size_of::()], - &mut drm_sys::drm_get_cap, - >(payload.as_mut_array().unwrap()) - }; - payload.value = self.adapter.get_cap( - payload - .capability - .try_into() - .map_err(|_| syscall::Error::new(EINVAL))?, - )?; - Ok(size_of::()) - } - ipc::SET_CLIENT_CAP => { - if payload.len() < size_of::() { - return Err(Error::new(EINVAL)); - } - let payload = unsafe { - transmute::< - &mut [u8; size_of::()], - &mut drm_sys::drm_set_client_cap, - >(payload.as_mut_array().unwrap()) - }; + Ok(0) + }), + ipc::GET_CAP => ipc::DrmGetCap::with(payload, |mut data| { + data.set_value( + self.adapter.get_cap( + data.capability() + .try_into() + .map_err(|_| syscall::Error::new(EINVAL))?, + )?, + ); + Ok(0) + }), + ipc::SET_CLIENT_CAP => ipc::DrmSetClientCap::with(payload, |data| { self.adapter.set_client_cap( - payload - .capability + data.capability() .try_into() .map_err(|_| syscall::Error::new(EINVAL))?, - payload.value, + data.value(), )?; - Ok(size_of::()) - } + Ok(0) + }), ipc::DISPLAY_COUNT => { if payload.len() < size_of::() { return Err(Error::new(EINVAL)); diff --git a/drivers/graphics/graphics-ipc/Cargo.toml b/drivers/graphics/graphics-ipc/Cargo.toml index cd9c02879e..47f98f1848 100644 --- a/drivers/graphics/graphics-ipc/Cargo.toml +++ b/drivers/graphics/graphics-ipc/Cargo.toml @@ -7,5 +7,6 @@ edition = "2021" drm-sys = "0.8.0" log = "0.4" libredox = "0.1.3" +redox_syscall = "0.5" common = { path = "../../common" } diff --git a/drivers/graphics/graphics-ipc/src/lib.rs b/drivers/graphics/graphics-ipc/src/lib.rs index ed4b73a675..fa4553bd0b 100644 --- a/drivers/graphics/graphics-ipc/src/lib.rs +++ b/drivers/graphics/graphics-ipc/src/lib.rs @@ -1,3 +1,5 @@ +#![feature(macro_metavar_expr_concat)] + mod common; pub mod v1; pub mod v2; diff --git a/drivers/graphics/graphics-ipc/src/v2.rs b/drivers/graphics/graphics-ipc/src/v2.rs index 80292da55e..1b671458d3 100644 --- a/drivers/graphics/graphics-ipc/src/v2.rs +++ b/drivers/graphics/graphics-ipc/src/v2.rs @@ -153,24 +153,232 @@ impl V2GraphicsHandle { pub mod ipc { use crate::common::Damage; + use core::cmp; + use core::ffi::{c_char, c_int}; + use core::iter; + use core::mem; + use core::slice; + + pub trait IoctlData { + unsafe fn write(&self) -> Vec; + 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 { + let noncounted_fields = ${concat(__, $mem_ty, Noncounted)} { + $($noncounted_field: self.$noncounted_field,)* + }; + // FIXME use Vec::with_capacity + let mut data = Vec::::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, + ) -> syscall::Result { + 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], + date_len: drm_sys::__kernel_size_t, + date: *mut c_char [array], + desc_len: drm_sys::__kernel_size_t, + desc: *mut c_char [array], + } } 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], + crtc_id_ptr: u64 [array], + connector_id_ptr: u64 [array], + encoder_id_ptr: u64 [array], + 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;