diff --git a/src/kernel/src/fs/file.rs b/src/kernel/src/fs/file.rs index 95069aa4a..b0e04d078 100644 --- a/src/kernel/src/fs/file.rs +++ b/src/kernel/src/fs/file.rs @@ -1,10 +1,11 @@ use super::{IoCmd, Offset, Stat, Uio, UioMut, Vnode}; use crate::dmem::BlockPool; use crate::errno::Errno; -use crate::errno::{ENOTTY, ENXIO}; +use crate::errno::{ENOTTY, ENXIO, EOPNOTSUPP}; use crate::kqueue::KernelQueue; use crate::net::Socket; use crate::process::VThread; +use crate::shm::Shm; use bitflags::bitflags; use macros::Errno; use std::fmt::Debug; @@ -85,6 +86,7 @@ impl VFile { VFileType::Vnode(vn) => vn.read(self, buf, td), VFileType::Socket(so) | VFileType::IpcSocket(so) => so.read(self, buf, td), VFileType::KernelQueue(kq) => kq.read(self, buf, td), + VFileType::SharedMemory(shm) => shm.read(self, buf, td), VFileType::Blockpool(bp) => bp.read(self, buf, td), } } @@ -94,6 +96,7 @@ impl VFile { VFileType::Vnode(vn) => vn.write(self, buf, td), VFileType::Socket(so) | VFileType::IpcSocket(so) => so.write(self, buf, td), VFileType::KernelQueue(kq) => kq.write(self, buf, td), + VFileType::SharedMemory(shm) => shm.write(self, buf, td), VFileType::Blockpool(bp) => bp.write(self, buf, td), } } @@ -104,6 +107,7 @@ impl VFile { 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::SharedMemory(shm) => shm.ioctl(self, cmd, td), VFileType::Blockpool(bp) => bp.ioctl(self, cmd, td), } } @@ -113,6 +117,7 @@ impl VFile { VFileType::Vnode(vn) => vn.stat(self, td), VFileType::Socket(so) | VFileType::IpcSocket(so) => so.stat(self, td), VFileType::KernelQueue(kq) => kq.stat(self, td), + VFileType::SharedMemory(shm) => shm.stat(self, td), VFileType::Blockpool(bp) => bp.stat(self, td), } } @@ -150,6 +155,7 @@ pub enum VFileType { Vnode(Arc), // DTYPE_VNODE = 1 Socket(Arc), // DTYPE_SOCKET = 2, KernelQueue(Arc), // DTYPE_KQUEUE = 5, + SharedMemory(Arc), // DTYPE_SHM = 8, IpcSocket(Arc), // DTYPE_IPCSOCKET = 15, Blockpool(Arc), // DTYPE_BLOCKPOOL = 17, } @@ -212,4 +218,8 @@ pub enum DefaultError { #[error("iocll is not supported")] #[errno(ENOTTY)] IoctlNotSupported, + + #[error("operation is not supported")] + #[errno(EOPNOTSUPP)] + OperationNotSupported, } diff --git a/src/kernel/src/fs/mod.rs b/src/kernel/src/fs/mod.rs index 961bc34ad..cdbf4b830 100644 --- a/src/kernel/src/fs/mod.rs +++ b/src/kernel/src/fs/mod.rs @@ -845,13 +845,17 @@ impl Fs { bitflags! { /// Flags for [`Fs::sys_open()`]. + #[derive(Clone, Copy, PartialEq, Eq)] pub struct OpenFlags: u32 { + const O_RDONLY = 0x00000000; const O_WRONLY = 0x00000001; const O_RDWR = 0x00000002; const O_ACCMODE = Self::O_WRONLY.bits() | Self::O_RDWR.bits(); const O_SHLOCK = 0x00000010; const O_EXLOCK = 0x00000020; + const O_CREAT = 0x00000200; const O_TRUNC = 0x00000400; + const O_EXCL = 0x00000800; const O_EXEC = 0x00040000; const O_CLOEXEC = 0x00100000; const UNK1 = 0x00400000; @@ -860,7 +864,7 @@ bitflags! { impl OpenFlags { /// An implementation of `FFLAGS` macro. - fn into_fflags(self) -> VFileFlags { + pub fn into_fflags(self) -> VFileFlags { VFileFlags::from_bits_truncate(self.bits() + 1) } } diff --git a/src/kernel/src/fs/path.rs b/src/kernel/src/fs/path.rs index caab88b20..95cbd2c87 100644 --- a/src/kernel/src/fs/path.rs +++ b/src/kernel/src/fs/path.rs @@ -274,6 +274,12 @@ impl Borrow for VPathBuf { } } +impl PartialEq for VPathBuf { + fn eq(&self, other: &VPath) -> bool { + self.0 == other.0 + } +} + impl Display for VPathBuf { fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result { self.0.fmt(f) diff --git a/src/kernel/src/fs/perm.rs b/src/kernel/src/fs/perm.rs index f3fdcd55a..55a77f33d 100644 --- a/src/kernel/src/fs/perm.rs +++ b/src/kernel/src/fs/perm.rs @@ -140,6 +140,7 @@ bitflags! { pub struct Access: u32 { const EXEC = 0o00000000100; // VEXEC const WRITE = 0o00000000200; // VWRITE + const READ = 0o00000000400; // VREAD const ADMIN = 0o00000010000; // VADMIN const EXPLICIT_DENY = 0o00000100000; // VEXPLICIT_DENY const DELETE_CHILD = 0o00001000000; // VDELETE_CHILD diff --git a/src/kernel/src/fs/stat.rs b/src/kernel/src/fs/stat.rs index ca5b598be..d6697ab0a 100644 --- a/src/kernel/src/fs/stat.rs +++ b/src/kernel/src/fs/stat.rs @@ -13,7 +13,7 @@ pub struct Stat { atime: TimeSpec, mtime: TimeSpec, ctime: TimeSpec, - size: i64, + pub size: i64, block_count: i64, pub block_size: u32, flags: u32, diff --git a/src/kernel/src/main.rs b/src/kernel/src/main.rs index f88a8f3c0..15e50c8da 100644 --- a/src/kernel/src/main.rs +++ b/src/kernel/src/main.rs @@ -13,6 +13,7 @@ use crate::osem::OsemManager; use crate::process::{VProc, VProcInitError, VThread}; use crate::regmgr::RegMgr; use crate::rtld::{LoadFlags, ModuleFlags, RuntimeLinker}; +use crate::shm::SharedMemoryManager; use crate::syscalls::Syscalls; use crate::sysctl::Sysctl; use crate::time::TimeManager; @@ -51,6 +52,7 @@ mod osem; mod process; mod regmgr; mod rtld; +mod shm; mod signal; mod syscalls; mod sysctl; @@ -269,6 +271,7 @@ fn run( let budget = BudgetManager::new(&mut syscalls); DmemManager::new(fs, &mut syscalls); + SharedMemoryManager::new(mm, &mut syscalls); Sysctl::new(mm, &machdep, &mut syscalls); TimeManager::new(&mut syscalls); KernelQueueManager::new(&mut syscalls); diff --git a/src/kernel/src/process/filedesc.rs b/src/kernel/src/process/filedesc.rs index 1b313180e..e546c84b5 100644 --- a/src/kernel/src/process/filedesc.rs +++ b/src/kernel/src/process/filedesc.rs @@ -17,6 +17,7 @@ pub struct FileDesc { cwd: Gutex>, // fd_cdir root: Gutex>, // fd_rdir kqueue_list: Gutex>>, // fd_kqlist + cmask: u32, // fd_cmask } impl FileDesc { @@ -29,6 +30,7 @@ impl FileDesc { cwd: gg.spawn(root.clone()), root: gg.spawn(root), kqueue_list: gg.spawn(VecDeque::new()), + cmask: 0o22, // TODO: verify this }; Arc::new(filedesc) @@ -46,6 +48,10 @@ impl FileDesc { self.kqueue_list.write().push_front(kq); } + pub fn cmask(&self) -> u32 { + self.cmask + } + #[allow(unused_variables)] // TODO: remove when implementing; add budget argument pub fn alloc_with_budget( &self, @@ -56,6 +62,15 @@ impl FileDesc { todo!() } + #[allow(unused_variables)] // TODO: remove when implementing; + pub fn alloc_without_budget( + &self, + constructor: impl FnOnce(i32) -> Result, + flags: VFileFlags, + ) -> Result> { + todo!() + } + /// See `finstall` on the PS4 for a reference. pub fn alloc(&self, file: Arc) -> i32 { // TODO: Implement fdalloc. diff --git a/src/kernel/src/process/thread.rs b/src/kernel/src/process/thread.rs index 3fe889c85..3e3b98050 100644 --- a/src/kernel/src/process/thread.rs +++ b/src/kernel/src/process/thread.rs @@ -1,4 +1,5 @@ use super::{CpuMask, CpuSet, VProc, NEXT_ID}; +use crate::errno::Errno; use crate::fs::VFile; use crate::signal::SignalSet; use crate::ucred::{Privilege, PrivilegeError, Ucred}; @@ -8,6 +9,7 @@ use llt::{OsThread, SpawnError}; use std::num::NonZeroI32; use std::sync::atomic::Ordering; use std::sync::Arc; +use thiserror::Error; use tls::{Local, Tls}; /// An implementation of `thread` structure for the main application. @@ -182,3 +184,12 @@ impl Drop for Running { } static VTHREAD: Tls> = Tls::new(); + +#[derive(Debug, Error)] +pub enum FileAllocError {} + +impl Errno for FileAllocError { + fn errno(&self) -> NonZeroI32 { + match *self {} + } +} diff --git a/src/kernel/src/shm/mod.rs b/src/kernel/src/shm/mod.rs new file mode 100644 index 000000000..407e355f2 --- /dev/null +++ b/src/kernel/src/shm/mod.rs @@ -0,0 +1,149 @@ +use crate::{ + errno::{Errno, EINVAL}, + fs::{ + check_access, Access, DefaultError, FileBackend, IoCmd, Mode, OpenFlags, Stat, Uio, UioMut, + VFile, VFileFlags, VPathBuf, + }, + memory::MemoryManager, + process::VThread, + syscalls::{SysErr, SysIn, SysOut, Syscalls}, + ucred::{Gid, Ucred, Uid}, +}; +use std::{convert::Infallible, sync::Arc}; + +pub struct SharedMemoryManager { + mm: Arc, +} + +impl SharedMemoryManager { + pub fn new(mm: &Arc, sys: &mut Syscalls) -> Arc { + let shm = Arc::new(Self { mm: mm.clone() }); + + sys.register(482, &shm, Self::sys_shm_open); + sys.register(483, &shm, Self::sys_shm_unlink); + + shm + } + + fn sys_shm_open(self: &Arc, i: &SysIn) -> Result { + let path = unsafe { i.args[0].to_shm_path() }?.expect("invalid shm path"); + let flags: OpenFlags = i.args[1].try_into().unwrap(); + let mode: u32 = i.args[2].try_into().unwrap(); + + if (flags & OpenFlags::O_ACCMODE != OpenFlags::O_RDONLY) + || (flags & OpenFlags::O_ACCMODE != OpenFlags::O_RDWR) + { + return Err(SysErr::Raw(EINVAL)); + } + + if !flags + .difference( + OpenFlags::O_ACCMODE | OpenFlags::O_CREAT | OpenFlags::O_EXCL | OpenFlags::O_TRUNC, + ) + .is_empty() + { + return Err(SysErr::Raw(EINVAL)); + } + + let td = VThread::current().unwrap(); + + let filedesc = td.proc().files(); + + let mode = mode & filedesc.cmask() & 0o7777; + + let fd = filedesc.alloc_without_budget::( + |_| match path { + ShmPath::Anon => { + todo!() + } + ShmPath::Path(path) => { + todo!() + } + }, + (flags & OpenFlags::O_ACCMODE).into_fflags(), + )?; + + Ok(fd.into()) + } + + fn sys_shm_unlink(self: &Arc, i: &SysIn) -> Result { + todo!("sys_shm_unlink") + } +} + +pub enum ShmPath { + Anon, + Path(VPathBuf), +} + +#[derive(Debug)] +pub struct Shm { + uid: Uid, + gid: Gid, + mode: Mode, +} + +impl Shm { + /// See `shm_do_truncate` on the PS4 for a reference. + fn truncate(&self, size: usize) { + todo!() + } + + /// See `shm_access` on the PS4 for a reference. + fn access(&self, cred: &Ucred, flags: VFileFlags) -> Result<(), Box> { + let mut access = Access::empty(); + + if flags.intersects(VFileFlags::READ) { + access |= Access::READ; + } + + if flags.intersects(VFileFlags::WRITE) { + access |= Access::WRITE; + } + + check_access(cred, self.uid, self.gid, self.mode, access, false)?; + + Ok(()) + } +} + +impl FileBackend for Shm { + #[allow(unused_variables)] + fn read( + self: &Arc, + file: &VFile, + buf: &mut UioMut, + td: Option<&VThread>, + ) -> Result> { + Err(DefaultError::OperationNotSupported.into()) + } + + #[allow(unused_variables)] + fn write( + self: &Arc, + file: &VFile, + buf: &mut Uio, + td: Option<&VThread>, + ) -> Result> { + Err(DefaultError::OperationNotSupported.into()) + } + + #[allow(unused_variables)] // remove when implementing + fn ioctl( + self: &Arc, + file: &VFile, + cmd: IoCmd, + td: Option<&VThread>, + ) -> Result<(), Box> { + todo!() + } + + #[allow(unused_variables)] // remove when implementing + fn stat(self: &Arc, file: &VFile, td: Option<&VThread>) -> Result> { + let mut stat = Stat::zeroed(); + + stat.block_size = 0x4000; + + todo!() + } +} diff --git a/src/kernel/src/syscalls/input.rs b/src/kernel/src/syscalls/input.rs index 8deaf44b1..636809aac 100644 --- a/src/kernel/src/syscalls/input.rs +++ b/src/kernel/src/syscalls/input.rs @@ -1,6 +1,7 @@ use super::SysErr; use crate::errno::{ENAMETOOLONG, ENOENT}; use crate::fs::{VPath, VPathBuf}; +use crate::shm::ShmPath; use std::ffi::{c_char, CStr}; use std::fmt::{Formatter, LowerHex}; use std::num::TryFromIntError; @@ -38,6 +39,23 @@ impl SysArg { Ok(Some(path)) } + pub unsafe fn to_shm_path(self) -> Result, SysErr> { + match self.0 { + 1 => Ok(Some(ShmPath::Anon)), + ptr => { + let slice = unsafe { std::slice::from_raw_parts(ptr as *const u8, 0x400) }; + + let cstr = + CStr::from_bytes_until_nul(slice).map_err(|_| SysErr::Raw(ENAMETOOLONG))?; + + let path = VPath::new(cstr.to_string_lossy().as_ref()) + .map(|p| ShmPath::Path(p.to_owned())); + + Ok(path) + } + } + } + /// See `copyinstr` on the PS4 for a reference. pub unsafe fn to_str<'a>(self, max: usize) -> Result, SysErr> { if self.0 == 0 { diff --git a/src/kernel/src/ucred/mod.rs b/src/kernel/src/ucred/mod.rs index ca19cfa18..b452b432b 100644 --- a/src/kernel/src/ucred/mod.rs +++ b/src/kernel/src/ucred/mod.rs @@ -40,6 +40,10 @@ impl Ucred { self.real_uid } + pub fn groups(&self) -> &[Gid] { + &self.groups + } + pub fn auth(&self) -> &AuthInfo { &self.auth }