Introduce infrastructure for serializing ioctls
And use it for a couple of drm interfaces
This commit is contained in:
Generated
+1
@@ -869,6 +869,7 @@ dependencies = [
|
||||
"drm-sys",
|
||||
"libredox",
|
||||
"log",
|
||||
"redox_syscall",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
|
||||
@@ -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<T: GraphicsAdapter> SchemeSync for GraphicsScheme<T> {
|
||||
return Err(Error::new(EOPNOTSUPP));
|
||||
}
|
||||
Handle::V2 { vt, next_id, fbs } => match metadata[0] {
|
||||
ipc::VERSION => {
|
||||
if payload.len() < size_of::<ipc::Version>() {
|
||||
return Err(Error::new(EINVAL));
|
||||
}
|
||||
let payload = unsafe {
|
||||
transmute::<&mut [u8; size_of::<ipc::Version>()], &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::Version>())
|
||||
}
|
||||
ipc::GET_CAP => {
|
||||
if payload.len() < size_of::<drm_sys::drm_get_cap>() {
|
||||
return Err(Error::new(EINVAL));
|
||||
}
|
||||
let payload = unsafe {
|
||||
transmute::<
|
||||
&mut [u8; size_of::<drm_sys::drm_get_cap>()],
|
||||
&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::<drm_sys::drm_get_cap>())
|
||||
}
|
||||
ipc::SET_CLIENT_CAP => {
|
||||
if payload.len() < size_of::<drm_sys::drm_set_client_cap>() {
|
||||
return Err(Error::new(EINVAL));
|
||||
}
|
||||
let payload = unsafe {
|
||||
transmute::<
|
||||
&mut [u8; size_of::<drm_sys::drm_set_client_cap>()],
|
||||
&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::<drm_sys::drm_set_client_cap>())
|
||||
}
|
||||
Ok(0)
|
||||
}),
|
||||
ipc::DISPLAY_COUNT => {
|
||||
if payload.len() < size_of::<ipc::DisplayCount>() {
|
||||
return Err(Error::new(EINVAL));
|
||||
|
||||
@@ -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" }
|
||||
|
||||
@@ -1,3 +1,5 @@
|
||||
#![feature(macro_metavar_expr_concat)]
|
||||
|
||||
mod common;
|
||||
pub mod v1;
|
||||
pub mod v2;
|
||||
|
||||
@@ -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<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;
|
||||
|
||||
Reference in New Issue
Block a user