diff --git a/docs/src/kernel/linux-compatibility.md b/docs/src/kernel/linux-compatibility.md index 81d10031..0549bb52 100644 --- a/docs/src/kernel/linux-compatibility.md +++ b/docs/src/kernel/linux-compatibility.md @@ -86,7 +86,7 @@ provided by Linux on x86-64 architecture. | 63 | uname | ✅ | | 64 | semget | ✅ | | 65 | semop | ✅ | -| 66 | semctl | ❌ | +| 66 | semctl | ✅ | | 67 | shmdt | ❌ | | 68 | msgget | ❌ | | 69 | msgsnd | ❌ | diff --git a/kernel/aster-nix/src/syscall/arch/x86.rs b/kernel/aster-nix/src/syscall/arch/x86.rs index ca03be57..075073e4 100644 --- a/kernel/aster-nix/src/syscall/arch/x86.rs +++ b/kernel/aster-nix/src/syscall/arch/x86.rs @@ -89,6 +89,7 @@ use crate::syscall::{ sched_getaffinity::sys_sched_getaffinity, sched_yield::sys_sched_yield, select::sys_select, + semctl::sys_semctl, semget::sys_semget, semop::{sys_semop, sys_semtimedop}, sendfile::sys_sendfile, @@ -194,6 +195,7 @@ impl_syscall_nums_and_dispatch_fn! { SYS_UNAME = 63 => sys_uname(args[..1]); SYS_SEMGET = 64 => sys_semget(args[..3]); SYS_SEMOP = 65 => sys_semop(args[..3]); + SYS_SEMCTL = 66 => sys_semctl(args[..4]); SYS_FCNTL = 72 => sys_fcntl(args[..3]); SYS_FLOCK = 73 => sys_flock(args[..2]); SYS_FSYNC = 74 => sys_fsync(args[..1]); diff --git a/kernel/aster-nix/src/syscall/mod.rs b/kernel/aster-nix/src/syscall/mod.rs index 1235f567..930f0e76 100644 --- a/kernel/aster-nix/src/syscall/mod.rs +++ b/kernel/aster-nix/src/syscall/mod.rs @@ -96,6 +96,7 @@ mod rt_sigsuspend; mod sched_getaffinity; mod sched_yield; mod select; +mod semctl; mod semget; mod semop; mod sendfile; diff --git a/kernel/aster-nix/src/syscall/semctl.rs b/kernel/aster-nix/src/syscall/semctl.rs new file mode 100644 index 00000000..62157051 --- /dev/null +++ b/kernel/aster-nix/src/syscall/semctl.rs @@ -0,0 +1,121 @@ +// SPDX-License-Identifier: MPL-2.0 + +use super::SyscallReturn; +use crate::{ + ipc::{ + semaphore::system_v::{ + sem_set::{check_sem, sem_sets, sem_sets_mut, SemaphoreSet}, + PermissionMode, + }, + IpcControlCmd, + }, + prelude::*, + process::Pid, +}; + +pub fn sys_semctl( + semid: i32, + semnum: i32, + cmd: i32, + arg: Vaddr, + ctx: &Context, +) -> Result { + if semid <= 0 || semnum < 0 { + return_errno!(Errno::EINVAL) + } + + let cmd = IpcControlCmd::try_from(cmd)?; + debug!( + "[sys_semctl] semid = {}, semnum = {}, cmd = {:?}, arg = {:x}", + semid, semnum, cmd, arg + ); + + match cmd { + IpcControlCmd::IPC_RMID => { + let mut sem_sets_mut = sem_sets_mut(); + let sem_set = sem_sets_mut.get(&semid).ok_or(Error::new(Errno::EINVAL))?; + + let euid = ctx.posix_thread.credentials().euid(); + let permission = sem_set.permission(); + let can_removed = (euid == permission.uid()) || (euid == permission.cuid()); + if !can_removed { + return_errno!(Errno::EPERM); + } + + sem_sets_mut + .remove(&semid) + .ok_or(Error::new(Errno::EINVAL))?; + } + IpcControlCmd::SEM_SETVAL => { + // In setval, arg is parse as i32 + let val = arg as i32; + if val < 0 { + return_errno!(Errno::ERANGE); + } + + check_and_ctl(semid, PermissionMode::ALTER, |sem_set| { + let sem = sem_set + .get(semnum as usize) + .ok_or(Error::new(Errno::EINVAL))?; + + sem.set_val(val)?; + sem_set.update_ctime(); + + Ok(()) + })?; + } + IpcControlCmd::SEM_GETVAL => { + let val: i32 = check_and_ctl(semid, PermissionMode::READ, |sem_set| { + Ok(sem_set + .get(semnum as usize) + .ok_or(Error::new(Errno::EINVAL))? + .val()) + })?; + + return Ok(SyscallReturn::Return(val as isize)); + } + IpcControlCmd::SEM_GETPID => { + let pid: Pid = check_and_ctl(semid, PermissionMode::READ, |sem_set| { + Ok(sem_set + .get(semnum as usize) + .ok_or(Error::new(Errno::EINVAL))? + .last_modified_pid()) + })?; + + return Ok(SyscallReturn::Return(pid as isize)); + } + IpcControlCmd::SEM_GETZCNT => { + let cnt: usize = check_and_ctl(semid, PermissionMode::READ, |sem_set| { + Ok(sem_set + .get(semnum as usize) + .ok_or(Error::new(Errno::EINVAL))? + .pending_zero_count()) + })?; + + return Ok(SyscallReturn::Return(cnt as isize)); + } + IpcControlCmd::SEM_GETNCNT => { + let cnt: usize = check_and_ctl(semid, PermissionMode::READ, |sem_set| { + Ok(sem_set + .get(semnum as usize) + .ok_or(Error::new(Errno::EINVAL))? + .pending_alter_count()) + })?; + + return Ok(SyscallReturn::Return(cnt as isize)); + } + _ => todo!("Need to support {:?} in SYS_SEMCTL", cmd), + } + + Ok(SyscallReturn::Return(0)) +} + +fn check_and_ctl(semid: i32, permission: PermissionMode, ctl_func: F) -> Result +where + F: FnOnce(&SemaphoreSet) -> Result, +{ + check_sem(semid, None, permission)?; + let sem_sets = sem_sets(); + let sem_set = sem_sets.get(&semid).ok_or(Error::new(Errno::EINVAL))?; + ctl_func.call_once((sem_set,)) +}