diff --git a/src/kernel/src/fs/ioctl.rs b/src/kernel/src/fs/ioctl.rs index ff11048c5..250c78c3e 100644 --- a/src/kernel/src/fs/ioctl.rs +++ b/src/kernel/src/fs/ioctl.rs @@ -1,5 +1,7 @@ use crate::errno::ENOTTY; +use crate::syscalls::SysArg; use crate::syscalls::SysErr; +use std::convert::Into; /// 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. @@ -43,7 +45,9 @@ macro_rules! commands { pub const IOC_OUT: u32 = 0x40000000; pub const IOC_IN: u32 = 0x80000000; - pub fn try_from_raw_parts(cmd: u64, arg: *mut u8) -> Result { + /// # Safety + /// `arg` has to be a pointer to the correct value + pub unsafe fn try_from_raw_parts(cmd: u64, arg: RawCommandArg) -> Result { let cmd = cmd as u32; if Self::is_invalid(cmd) { @@ -51,7 +55,7 @@ macro_rules! commands { } let cmd = match cmd { - $( $value => Self::$variant $( ( unsafe { &mut *(arg as *mut $type) } ) )? ,)* + $( $value => Self::$variant $( ( unsafe { &mut *(arg.ptr() as *mut $type) } ) )? ,)* _ => todo!("Unhandled ioctl command {:#x}", cmd) }; @@ -102,3 +106,27 @@ commands! { DIOCGMEDIASIZE(&i64) = 0x40086418, } } + +pub struct RawCommandArg<'a> { + ptr: *mut u8, + _phantom: std::marker::PhantomData<&'a u8>, +} + +impl<'a> RawCommandArg<'a> { + pub fn ptr(&self) -> *mut u8 { + self.ptr + } + + pub fn is_null(&self) -> bool { + self.ptr.is_null() + } +} + +impl From for RawCommandArg<'_> { + fn from(arg: SysArg) -> Self { + Self { + ptr: arg.into(), + _phantom: std::marker::PhantomData, + } + } +} diff --git a/src/kernel/src/fs/mod.rs b/src/kernel/src/fs/mod.rs index f800292ca..5144d73c4 100644 --- a/src/kernel/src/fs/mod.rs +++ b/src/kernel/src/fs/mod.rs @@ -444,8 +444,11 @@ impl Fs { fn sys_ioctl(self: &Arc, td: &VThread, i: &SysIn) -> Result { let fd: i32 = i.args[0].try_into().unwrap(); + let cmd: u64 = i.args[1].into(); + let arg: RawCommandArg = i.args[2].into(); + // Our IoCmd contains both the command and the argument (if there is one). - let cmd = IoCmd::try_from_raw_parts(i.args[1].into(), i.args[2].into())?; + let cmd = unsafe { IoCmd::try_from_raw_parts(cmd, arg)? }; info!("Executing ioctl({cmd:?}) on file descriptor {fd}."); diff --git a/src/kernel/src/regmgr/command.rs b/src/kernel/src/regmgr/command.rs new file mode 100644 index 000000000..d3f3f1886 --- /dev/null +++ b/src/kernel/src/regmgr/command.rs @@ -0,0 +1,37 @@ +use super::RegError; +use crate::fs::RawCommandArg; + +#[repr(u32)] +pub(super) enum RegMgrCommand<'a> { + SetInt(&'a SetIntArg) = 0x18, + Unk1(&'a Unk1Arg) = 0x19, +} +impl<'a> RegMgrCommand<'a> { + /// # Safety + /// `arg` has to be a pointer to the correct value + pub unsafe fn try_from_raw_parts(cmd: u32, arg: RawCommandArg) -> Result { + match cmd { + 0x18 => Ok(RegMgrCommand::SetInt(unsafe { + &*(arg.ptr() as *mut SetIntArg) + })), + 0x19 => Ok(RegMgrCommand::Unk1(unsafe { + &*(arg.ptr() as *mut Unk1Arg) + })), + 0x27 | 0x40.. => Err(RegError::V800d0219), + v => todo!("RegMgrCommand({v})"), + } + } +} + +#[repr(C)] +pub(super) struct SetIntArg { + pub v1: u64, + pub v2: u32, + pub value: i32, +} + +#[repr(C)] +pub(super) struct Unk1Arg { + pub v1: u64, + pub v2: u32, +} diff --git a/src/kernel/src/regmgr/mod.rs b/src/kernel/src/regmgr/mod.rs index 4679837d4..361e1543b 100644 --- a/src/kernel/src/regmgr/mod.rs +++ b/src/kernel/src/regmgr/mod.rs @@ -1,17 +1,19 @@ -pub use self::key::*; - +use self::command::*; use crate::errno::EINVAL; +use crate::fs::RawCommandArg; use crate::process::VThread; use crate::syscalls::{SysErr, SysIn, SysOut, Syscalls}; -use crate::ucred::Ucred; +use crate::ucred::{Privilege, Ucred}; use crate::{info, warn}; use std::fmt::{Display, Formatter}; use std::num::NonZeroI32; use std::ops::Index; -use std::ptr::read; use std::sync::Arc; use thiserror::Error; +pub use self::key::*; + +mod command; mod key; /// An implementation of PS4 registry manager. @@ -31,10 +33,13 @@ impl RegMgr { // Get arguments. let op: u32 = i.args[0].try_into().unwrap(); let buf: *mut i32 = i.args[2].into(); - let req: *const u8 = i.args[3].into(); + let req: RawCommandArg = i.args[3].into(); let reqlen: usize = i.args[4].into(); - // TODO: Check the result of priv_check(td, 682). + let td = VThread::current().unwrap(); + + td.cred().priv_check(Privilege::SCE682)?; + if buf.is_null() { todo!("regmgr_call with buf = null"); } @@ -47,12 +52,21 @@ impl RegMgr { todo!("regmgr_call with reqlen > 2048"); } + let command = match unsafe { RegMgrCommand::try_from_raw_parts(op, req) } { + Ok(v) => v, + Err(e) => { + warn!(e, "regmgr_call({op}) failed"); + unsafe { *buf = e.code() }; + return Ok(SysOut::ZERO); + } + }; + // Execute the operation. - let r = match op { - 0x18 => { - let v1 = unsafe { read::(req as _) }; - let v2 = unsafe { read::(req.add(8) as _) }; - let value = unsafe { read::(req.add(12) as _) }; + let r = match command { + RegMgrCommand::SetInt(arg) => { + let v1 = arg.v1; + let v2 = arg.v2; + let value = arg.value; info!( "Attempting to set registry with v1: {}, v2: {}, value: {}.", @@ -64,15 +78,13 @@ impl RegMgr { self.set_int(k, value) }) } - 0x19 => { - let v1 = unsafe { read::(req as _) }; - let v2 = unsafe { read::(req.add(8) as _) }; + RegMgrCommand::Unk1(arg) => { + let v1 = arg.v1; + let v2 = arg.v2; self.decode_key(v1, v2, td.cred(), 1) .and_then(|k| todo!("regmgr_call({op}) with matched key = {k}")) } - 0x27 | 0x40.. => Err(RegError::V800d0219), - v => todo!("regmgr_call({v})"), }; // Write the result. diff --git a/src/kernel/src/ucred/mod.rs b/src/kernel/src/ucred/mod.rs index b452b432b..0459d3071 100644 --- a/src/kernel/src/ucred/mod.rs +++ b/src/kernel/src/ucred/mod.rs @@ -99,6 +99,7 @@ impl Ucred { Privilege::MAXFILES | Privilege::PROC_SETLOGIN | Privilege::SCE680 + | Privilege::SCE682 | Privilege::SCE683 | Privilege::SCE686 => self.is_system(), v => todo!("priv_check_cred(cred, {v})"), diff --git a/src/kernel/src/ucred/privilege.rs b/src/kernel/src/ucred/privilege.rs index b3169580f..b05b345b8 100644 --- a/src/kernel/src/ucred/privilege.rs +++ b/src/kernel/src/ucred/privilege.rs @@ -8,33 +8,37 @@ use std::fmt::{Display, Formatter}; #[derive(Clone, Copy, PartialEq, Eq)] pub struct Privilege(i32); -impl Privilege { - pub const MAXFILES: Self = Self(3); - pub const PROC_SETLOGIN: Self = Self(161); - pub const VFS_READ: Self = Self(310); - pub const VFS_WRITE: Self = Self(311); - pub const VFS_ADMIN: Self = Self(312); - pub const VFS_EXEC: Self = Self(313); - pub const VFS_LOOKUP: Self = Self(314); - pub const SCE680: Self = Self(680); - pub const SCE683: Self = Self(683); - pub const SCE686: Self = Self(686); -} +macro_rules! privileges { + ($($name:ident($value:expr)),*) => { + impl Privilege { + $( + pub const $name: Self = Self($value); + )* + } -impl Display for Privilege { - fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result { - match *self { - Self::MAXFILES => f.write_str("PRIV_MAXFILES"), - Self::PROC_SETLOGIN => f.write_str("PRIV_PROC_SETLOGIN"), - Self::VFS_READ => f.write_str("PRIV_VFS_READ"), - Self::VFS_WRITE => f.write_str("PRIV_VFS_WRITE"), - Self::VFS_ADMIN => f.write_str("PRIV_VFS_ADMIN"), - Self::VFS_EXEC => f.write_str("PRIV_VFS_EXEC"), - Self::VFS_LOOKUP => f.write_str("PRIV_VFS_LOOKUP"), - Self::SCE680 => f.write_str("SCE680"), - Self::SCE683 => f.write_str("SCE683"), - Self::SCE686 => f.write_str("SCE686"), - v => v.0.fmt(f), + impl Display for Privilege { + fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result { + match *self { + $( + Self($value) => f.write_str(stringify!($name)), + )* + v => v.0.fmt(f), + } + } } } } + +privileges! { + MAXFILES(3), + PROC_SETLOGIN(161), + VFS_READ(310), + VFS_WRITE(311), + VFS_ADMIN(312), + VFS_EXEC(313), + VFS_LOOKUP(314), + SCE680(680), + SCE682(682), + SCE683(683), + SCE686(686) +}