-
Notifications
You must be signed in to change notification settings - Fork 18
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Partially implements sys_lseek, refactors ioctl commands (#648)
- Loading branch information
1 parent
ae65120
commit 2c3b381
Showing
12 changed files
with
199 additions
and
347 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,108 +1,104 @@ | ||
use crate::errno::ENOTTY; | ||
use crate::syscalls::{SysArg, SysErr}; | ||
use std::fmt::{Display, Formatter}; | ||
|
||
/// A wrapper type for and IOCTL command. | ||
/// FreeBSD uses an u_long, but masks off the top 4 bytes in kern_ioctl, so we can use an u32. | ||
#[repr(transparent)] | ||
#[derive(Debug, Clone, Copy, PartialEq, Eq)] | ||
pub struct IoCmd(u32); | ||
|
||
impl IoCmd { | ||
pub const IOCPARM_SHIFT: u32 = 13; | ||
pub const IOCPARM_MASK: u32 = (1 << Self::IOCPARM_SHIFT) - 1; | ||
pub const IOC_VOID: u32 = 0x20000000; | ||
pub const IOC_OUT: u32 = 0x40000000; | ||
pub const IOC_IN: u32 = 0x80000000; | ||
pub const IOC_INOUT: u32 = Self::IOC_IN | Self::IOC_OUT; | ||
|
||
pub const fn try_from_raw(com: u64) -> Option<Self> { | ||
let com = com as u32; | ||
|
||
if Self::is_invalid(com) { | ||
return None; | ||
} | ||
|
||
Some(Self(com)) | ||
} | ||
|
||
const fn new(inout: u32, group: u8, num: u8, len: usize) -> Self { | ||
let len: u32 = if len > (u32::MAX) as usize { | ||
panic!("IOCPARM_LEN is too large"); | ||
} else { | ||
len as u32 | ||
use crate::syscalls::SysErr; | ||
|
||
/// This macro does some compile time verification to ensure we don't mistype anything. | ||
/// It also ensures that we don't miss any commands, since [`IoCmd::try_from_raw_parts`] will panic with a todo! if it encounters an unknown command. | ||
/// | ||
/// # Note | ||
/// The `$hack` variable is used to provide a variable, because $(mut)? is has to contain a variable. It is used singly for this purpose and | ||
/// should not be messed with. | ||
macro_rules! commands { | ||
( | ||
$vis:vis enum $enum_name:ident { | ||
$( | ||
$( #[$attr:meta] )* | ||
$variant:ident $( (& $(mut $($hack:lifetime)? )? $type:ty) )? = $value:literal, | ||
)* | ||
} | ||
) => { | ||
/// A wrapper type for an ioctl command. | ||
/// FreeBSD uses an u_long, but masks off the top 4 bytes in kern_ioctl, so we can use an u32. | ||
#[derive(Debug)] | ||
#[non_exhaustive] | ||
#[repr(u32)] | ||
$vis enum $enum_name<'a> { | ||
$( | ||
$( #[$attr] )* | ||
$variant $( (&'a $(mut $($hack)? )? $type) )? = { | ||
assert!( !$enum_name::is_invalid($value) ); | ||
|
||
$( | ||
assert!(std::mem::size_of::<$type>() == IoCmd::iocparm_len($value)); | ||
)? | ||
|
||
$value | ||
}, | ||
)* | ||
} | ||
|
||
impl<'a> $enum_name<'a> { | ||
pub const IOCPARM_SHIFT: u32 = 13; | ||
pub const IOCPARM_MASK: u32 = (1 << Self::IOCPARM_SHIFT) - 1; | ||
pub const IOC_VOID: u32 = 0x20000000; | ||
pub const IOC_OUT: u32 = 0x40000000; | ||
pub const IOC_IN: u32 = 0x80000000; | ||
|
||
pub fn try_from_raw_parts(cmd: u64, arg: *mut u8) -> Result<Self, SysErr> { | ||
let cmd = cmd as u32; | ||
|
||
if Self::is_invalid(cmd) { | ||
return Err(SysErr::Raw(ENOTTY)); | ||
} | ||
|
||
let cmd = match cmd { | ||
$( $value => Self::$variant $( ( unsafe { &mut *(arg as *mut $type) } ) )? ,)* | ||
_ => todo!("Unhandled ioctl command {:#x}", cmd) | ||
}; | ||
|
||
Ok(cmd) | ||
} | ||
|
||
const fn is_invalid(com: u32) -> bool { | ||
if com & (Self::IOC_VOID | Self::IOC_IN | Self::IOC_OUT) == 0 { | ||
return true; | ||
} | ||
|
||
if com & (Self::IOC_IN | Self::IOC_OUT) != 0 && Self::iocparm_len(com) == 0 { | ||
return true; | ||
} | ||
|
||
if com & Self::IOC_VOID != 0 && Self::iocparm_len(com) != 0 && Self::iocparm_len(com) != 4 { | ||
return true; | ||
} | ||
|
||
false | ||
} | ||
|
||
const fn iocparm_len(com: u32) -> usize { | ||
((com >> 16) & Self::IOCPARM_MASK) as usize | ||
} | ||
} | ||
}; | ||
|
||
Self(inout | ((len & Self::IOCPARM_MASK) << 16) | ((group as u32) << 8) | (num as u32)) | ||
} | ||
|
||
pub fn size(&self) -> usize { | ||
Self::iocparm_len(self.0) | ||
} | ||
|
||
pub fn is_void(&self) -> bool { | ||
self.0 & Self::IOC_VOID != 0 | ||
} | ||
|
||
pub fn is_out(&self) -> bool { | ||
self.0 & Self::IOC_OUT != 0 | ||
} | ||
|
||
pub fn is_in(&self) -> bool { | ||
self.0 & Self::IOC_IN != 0 | ||
} | ||
|
||
const fn is_invalid(com: u32) -> bool { | ||
if com & (Self::IOC_VOID | Self::IOC_IN | Self::IOC_OUT) == 0 { | ||
return true; | ||
} | ||
|
||
if com & (Self::IOC_IN | Self::IOC_OUT) != 0 && Self::iocparm_len(com) == 0 { | ||
return true; | ||
} | ||
|
||
if com & Self::IOC_VOID != 0 && Self::iocparm_len(com) != 0 && Self::iocparm_len(com) != 4 { | ||
return true; | ||
} | ||
|
||
false | ||
} | ||
|
||
const fn iocparm_len(com: u32) -> usize { | ||
((com >> 16) & Self::IOCPARM_MASK) as usize | ||
} | ||
|
||
pub const fn io(group: u8, num: u8) -> Self { | ||
Self::new(Self::IOC_VOID, group, num, 0) | ||
} | ||
|
||
pub const fn iowint(group: u8, num: u8) -> Self { | ||
Self::new(Self::IOC_VOID, group, num, std::mem::size_of::<i32>()) | ||
} | ||
|
||
pub const fn ior<T>(group: u8, num: u8) -> Self { | ||
Self::new(Self::IOC_OUT, group, num, std::mem::size_of::<T>()) | ||
} | ||
|
||
pub const fn iow<T>(group: u8, num: u8) -> Self { | ||
Self::new(Self::IOC_IN, group, num, std::mem::size_of::<T>()) | ||
} | ||
|
||
pub const fn iowr<T>(group: u8, num: u8) -> Self { | ||
Self::new(Self::IOC_INOUT, group, num, std::mem::size_of::<T>()) | ||
} | ||
} | ||
|
||
impl TryFrom<SysArg> for IoCmd { | ||
type Error = SysErr; | ||
|
||
fn try_from(v: SysArg) -> Result<Self, Self::Error> { | ||
Self::try_from_raw(v.into()).ok_or(SysErr::Raw(ENOTTY)) | ||
} | ||
} | ||
|
||
impl Display for IoCmd { | ||
fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result { | ||
write!(f, "{:#x}", self.0) | ||
// TODO: implement void ioctl commands with int data | ||
|
||
commands! { | ||
pub enum IoCmd { | ||
/// Set close on exec on fd. | ||
FIOCLEX = 0x20006601, | ||
/// Remove close on exec on fd. | ||
FIONCLEX = 0x20006602, | ||
/// Set/clear non-blocking I/O. | ||
FIONBIO(&i32) = 0x8004667d, | ||
/// Set/clear async I/O. | ||
FIOASYNC(&i32) = 0x8004667e, | ||
/// Seek data. | ||
FIOSEEKDATA(&mut i64) = 0xC0086661, | ||
/// Seek hole. | ||
FIOSEEKHOLE(&mut i64) = 0xC0086662, | ||
/// Become controlling terminal. | ||
TIOCSCTTY = 0x20007461, | ||
/// Get media size in bytes. | ||
DIOCGMEDIASIZE(&i64) = 0x40086418, | ||
} | ||
} |
Oops, something went wrong.