Skip to content

Commit

Permalink
Partially implements sys_lseek, refactors ioctl commands (#648)
Browse files Browse the repository at this point in the history
  • Loading branch information
SuchAFuriousDeath authored Feb 16, 2024
1 parent ae65120 commit 2c3b381
Show file tree
Hide file tree
Showing 12 changed files with 199 additions and 347 deletions.
37 changes: 2 additions & 35 deletions src/kernel/src/dmem/blockpool.rs
Original file line number Diff line number Diff line change
@@ -1,9 +1,7 @@
use crate::errno::{Errno, ENOTTY};
use crate::errno::Errno;
use crate::fs::{FileBackend, IoCmd, Stat, VFile};
use crate::process::VThread;
use macros::Errno;
use std::sync::Arc;
use thiserror::Error;

#[derive(Debug)]
pub struct BlockPool {}
Expand All @@ -14,14 +12,9 @@ impl FileBackend for BlockPool {
self: &Arc<Self>,
file: &VFile,
cmd: IoCmd,
data: &mut [u8],
td: Option<&VThread>,
) -> Result<(), Box<dyn Errno>> {
match cmd {
BLOCKPOOL_CMD1 => todo!("blockpool ioctl cmd 1"),
BLOCKPOOL_CMD2 => todo!("blockpool ioctl cmd 2"),
_ => Err(IoctlError::InvalidCommand(cmd).into()),
}
todo!()
}

fn stat(self: &Arc<Self>, _: &VFile, _: Option<&VThread>) -> Result<Stat, Box<dyn Errno>> {
Expand All @@ -33,29 +26,3 @@ impl FileBackend for BlockPool {
todo!()
}
}

#[derive(Debug, Error, Errno)]
pub enum IoctlError {
#[error("invalid command {0}")]
#[errno(ENOTTY)]
InvalidCommand(IoCmd),
}

pub const BLOCKPOOL_CMD1: IoCmd = IoCmd::iowr::<BlockpoolCmd1Arg>(0xa8, 1);
pub const BLOCKPOOL_CMD2: IoCmd = IoCmd::ior::<BlockpoolCmd2Arg>(0xa8, 2);

#[repr(C)]
struct BlockpoolCmd1Arg {
arg1: u64,
arg2: u64,
arg3: u64,
arg4: u64,
}

#[repr(C)]
struct BlockpoolCmd2Arg {
arg1: u32,
arg2: u32,
arg3: u32,
arg4: u32,
}
1 change: 0 additions & 1 deletion src/kernel/src/fs/dev/vnode.rs
Original file line number Diff line number Diff line change
Expand Up @@ -102,7 +102,6 @@ impl crate::fs::VnodeBackend for VnodeBackend {
self: Arc<Self>,
#[allow(unused_variables)] vn: &Arc<Vnode>,
#[allow(unused_variables)] cmd: IoCmd,
#[allow(unused_variables)] data: &mut [u8],
#[allow(unused_variables)] td: Option<&VThread>,
) -> Result<(), Box<dyn Errno>> {
todo!()
Expand Down
33 changes: 9 additions & 24 deletions src/kernel/src/fs/file.rs
Original file line number Diff line number Diff line change
Expand Up @@ -35,6 +35,10 @@ impl VFile {
&mut self.flags
}

pub fn vnode(&self) -> Option<Arc<Vnode>> {
todo!()
}

/// See `dofileread` on the PS4 for a reference.
pub fn do_read(
&self,
Expand Down Expand Up @@ -95,17 +99,12 @@ impl VFile {
}

/// See `fo_ioctl` on the PS4 for a reference.
pub fn ioctl(
&self,
cmd: IoCmd,
data: &mut [u8],
td: Option<&VThread>,
) -> Result<(), Box<dyn Errno>> {
pub fn ioctl(&self, cmd: IoCmd, td: Option<&VThread>) -> Result<(), Box<dyn Errno>> {
match &self.backend {
VFileType::Vnode(vn) => vn.ioctl(self, cmd, data, td),
VFileType::Socket(so) | VFileType::IpcSocket(so) => so.ioctl(self, cmd, data, td),
VFileType::KernelQueue(kq) => kq.ioctl(self, cmd, data, td),
VFileType::Blockpool(bp) => bp.ioctl(self, cmd, data, td),
VFileType::Vnode(vn) => vn.ioctl(self, cmd, td),
VFileType::Socket(so) | VFileType::IpcSocket(so) => so.ioctl(self, cmd, td),
VFileType::KernelQueue(kq) => kq.ioctl(self, cmd, td),
VFileType::Blockpool(bp) => bp.ioctl(self, cmd, td),
}
}

Expand Down Expand Up @@ -147,7 +146,6 @@ impl Write for VFile {

/// Type of [`VFile`].
#[derive(Debug)]
#[rustfmt::skip]
pub enum VFileType {
Vnode(Arc<Vnode>), // DTYPE_VNODE = 1
Socket(Arc<Socket>), // DTYPE_SOCKET = 2,
Expand All @@ -165,14 +163,6 @@ bitflags! {
}
}

bitflags! {
#[derive(Debug, Clone, Copy)]
pub struct VFileOpsFlags: u32 {
const PASSABLE = 0x00000001; // DFLAG_PASSABLE
const SEEKABLE = 0x00000002; // DFLAG_SEEKABLE
}
}

/// An implementation of `fileops` structure.
pub trait FileBackend: Debug + Send + Sync + 'static {
#[allow(unused_variables)]
Expand Down Expand Up @@ -200,18 +190,13 @@ pub trait FileBackend: Debug + Send + Sync + 'static {
self: &Arc<Self>,
file: &VFile,
cmd: IoCmd,
data: &mut [u8],
td: Option<&VThread>,
) -> Result<(), Box<dyn Errno>> {
Err(Box::new(DefaultError::IoctlNotSupported))
}

#[allow(unused_variables)]
fn stat(self: &Arc<Self>, file: &VFile, td: Option<&VThread>) -> Result<Stat, Box<dyn Errno>>;

fn flags(&self) -> VFileOpsFlags {
VFileOpsFlags::empty()
}
}

#[derive(Debug, Error, Errno)]
Expand Down
1 change: 0 additions & 1 deletion src/kernel/src/fs/host/vnode.rs
Original file line number Diff line number Diff line change
Expand Up @@ -52,7 +52,6 @@ impl crate::fs::VnodeBackend for VnodeBackend {
self: Arc<Self>,
#[allow(unused_variables)] vn: &Arc<Vnode>,
#[allow(unused_variables)] cmd: IoCmd,
#[allow(unused_variables)] data: &mut [u8],
#[allow(unused_variables)] td: Option<&VThread>,
) -> Result<(), Box<dyn Errno>> {
todo!()
Expand Down
202 changes: 99 additions & 103 deletions src/kernel/src/fs/ioctl.rs
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,
}
}
Loading

0 comments on commit 2c3b381

Please sign in to comment.