From eb582650b4d2840fc28af14048e1804b4393dbbf Mon Sep 17 00:00:00 2001 From: mivik Date: Thu, 27 Mar 2025 00:31:53 +0800 Subject: [PATCH 01/16] feat: add basic signal support & test --- apps/libc/c/signal/signal.c | 97 ++++++++++++++ apps/libc/c/sleep/sleep.c | 4 +- apps/libc/expect_off.out | 19 ++- apps/libc/testcase_list | 1 + configs/aarch64.toml | 5 +- configs/dummy.toml | 2 + configs/loongarch64.toml | 3 + configs/riscv64.toml | 3 + configs/x86_64.toml | 5 +- src/main.rs | 23 +++- src/mm.rs | 2 +- src/signal/ctypes.rs | 167 +++++++++++++++++++++++ src/signal/mod.rs | 237 +++++++++++++++++++++++++++++++++ src/syscall_imp/mod.rs | 6 +- src/syscall_imp/signal.rs | 146 ++++++++++++++++++-- src/syscall_imp/task/thread.rs | 4 + src/task.rs | 29 ++-- 17 files changed, 714 insertions(+), 39 deletions(-) create mode 100644 apps/libc/c/signal/signal.c create mode 100644 src/signal/ctypes.rs create mode 100644 src/signal/mod.rs diff --git a/apps/libc/c/signal/signal.c b/apps/libc/c/signal/signal.c new file mode 100644 index 000000000..c68d71159 --- /dev/null +++ b/apps/libc/c/signal/signal.c @@ -0,0 +1,97 @@ +#include +#include +#include +#include + +void test_term() { + puts("test_term"); + if (fork() == 0) { + kill(0, SIGTERM); + puts("This should not be printed"); + } + wait(0); + puts("Done"); +} + +static void signal_handler(int signum) { + static int count = 0; + count++; + printf("Received signal %d, count=%d\n", signum, count); + if (count > 1) { + return; + } + // This should be blocked and won't cause recursion + kill(0, SIGTERM); + printf("End, count=%d\n", count); +} + +void test_sigaction() { + puts("test_sigaction"); + struct sigaction sa = {0}; + sa.sa_handler = signal_handler; + sa.sa_flags = 0; + sigaction(SIGTERM, &sa, NULL); + kill(0, SIGTERM); + puts("Ok1"); + + sa.sa_handler = (void (*)(int))1; + sigaction(SIGTERM, &sa, NULL); + kill(0, SIGTERM); + puts("Ok2"); + + sa.sa_handler = (void (*)(int))0; + sigaction(SIGTERM, &sa, NULL); +} + +void test_sigprocmask() { + puts("test_sigprocmask"); + sigset_t set, set2; + sigemptyset(&set); + sigaddset(&set, SIGTERM); + sigprocmask(SIG_BLOCK, &set, NULL); + kill(0, SIGTERM); + + sigpending(&set2); + if (sigismember(&set2, SIGTERM)) { + puts("Ok1"); + } + + // Ignore SIGTERM for once + struct sigaction sa = {0}; + sa.sa_handler = (void (*)(int))1; + sa.sa_flags = 0; + sigaction(SIGTERM, &sa, NULL); + + sigdelset(&set, SIGTERM); + sigprocmask(SIG_SETMASK, &set, NULL); + + sigpending(&set2); + if (!sigismember(&set2, SIGTERM)) { + puts("Ok2"); + } + + sa.sa_handler = (void (*)(int))0; + sigaction(SIGTERM, &sa, NULL); +} + +void test_sigkill_stop() { + puts("test_sigkill_stop"); + struct sigaction sa = {0}; + sa.sa_handler = signal_handler; + sa.sa_flags = 0; + if (sigaction(SIGKILL, &sa, NULL) == 0) { + puts("Wrong SIGKILL"); + } + if (sigaction(SIGSTOP, &sa, NULL) == 0) { + puts("Wrong SIGSTOP"); + } + puts("Done"); +} + +int main() { + test_term(); + test_sigaction(); + test_sigprocmask(); + test_sigkill_stop(); + return 0; +} \ No newline at end of file diff --git a/apps/libc/c/sleep/sleep.c b/apps/libc/c/sleep/sleep.c index d72f17268..71faffe04 100644 --- a/apps/libc/c/sleep/sleep.c +++ b/apps/libc/c/sleep/sleep.c @@ -2,8 +2,8 @@ #include int main() { - printf("Sleeping for 5 seconds...\n"); - sleep(5); + printf("Sleeping for 2 seconds...\n"); + sleep(2); printf("Done!\n"); return 0; } \ No newline at end of file diff --git a/apps/libc/expect_off.out b/apps/libc/expect_off.out index 0904c2685..7fedadbfd 100644 --- a/apps/libc/expect_off.out +++ b/apps/libc/expect_off.out @@ -3,5 +3,20 @@ build_mode = release log_level = off Hello, World! -Sleeping for 5 seconds... -Done! \ No newline at end of file +Sleeping for 2 seconds... +Done! + +test_term +Done +test_sigaction +Received signal 15, count=1 +End, count=1 +Received signal 15, count=2 +Ok1 +Ok2 +test_sigprocmask +Ok1 +Ok2 +Done +test_sigkill_stop +Done diff --git a/apps/libc/testcase_list b/apps/libc/testcase_list index 4e02956a8..105ab97aa 100644 --- a/apps/libc/testcase_list +++ b/apps/libc/testcase_list @@ -1,2 +1,3 @@ helloworld_c sleep_c +signal_c diff --git a/configs/aarch64.toml b/configs/aarch64.toml index 022e2b03e..26fa9f80a 100644 --- a/configs/aarch64.toml +++ b/configs/aarch64.toml @@ -17,4 +17,7 @@ user-heap-base = 0x4000_0000 user-heap-size = 0x1_0000 # The size of the kernel stack. -kernel-stack-size = 0x40000 \ No newline at end of file +kernel-stack-size = 0x40000 + +# The address of signal trampoline. +signal-trampoline = 0x4001_0000 diff --git a/configs/dummy.toml b/configs/dummy.toml index c23261c89..6f46d2e5d 100644 --- a/configs/dummy.toml +++ b/configs/dummy.toml @@ -54,6 +54,8 @@ user-heap-base = 0 # uint # The size of the user heap. user-heap-size = 0 # uint +# The address of signal trampoline. +signal-trampoline = 0 # # Device specifications diff --git a/configs/loongarch64.toml b/configs/loongarch64.toml index ea18784ad..066920e99 100644 --- a/configs/loongarch64.toml +++ b/configs/loongarch64.toml @@ -18,3 +18,6 @@ user-heap-size = 0x1_0000 # The size of the kernel stack. kernel-stack-size = 0x40000 + +# The address of signal trampoline. +signal-trampoline = 0x4001_0000 diff --git a/configs/riscv64.toml b/configs/riscv64.toml index ea18784ad..066920e99 100644 --- a/configs/riscv64.toml +++ b/configs/riscv64.toml @@ -18,3 +18,6 @@ user-heap-size = 0x1_0000 # The size of the kernel stack. kernel-stack-size = 0x40000 + +# The address of signal trampoline. +signal-trampoline = 0x4001_0000 diff --git a/configs/x86_64.toml b/configs/x86_64.toml index 022e2b03e..26fa9f80a 100644 --- a/configs/x86_64.toml +++ b/configs/x86_64.toml @@ -17,4 +17,7 @@ user-heap-base = 0x4000_0000 user-heap-size = 0x1_0000 # The size of the kernel stack. -kernel-stack-size = 0x40000 \ No newline at end of file +kernel-stack-size = 0x40000 + +# The address of signal trampoline. +signal-trampoline = 0x4001_0000 diff --git a/src/main.rs b/src/main.rs index 68a1896b3..f2109768c 100644 --- a/src/main.rs +++ b/src/main.rs @@ -10,6 +10,7 @@ mod ctypes; mod mm; mod ptr; +mod signal; mod syscall_imp; mod task; @@ -19,10 +20,10 @@ use alloc::{ vec::Vec, }; use axerrno::AxResult; -use axhal::arch::UspaceContext; +use axhal::{arch::UspaceContext, mem::virt_to_phys, paging::MappingFlags}; use axmm::{AddrSpace, kernel_aspace}; use axsync::Mutex; -use memory_addr::VirtAddr; +use memory_addr::{PAGE_SIZE_4K, VirtAddr}; fn new_user_aspace_empty() -> AxResult { AddrSpace::new_empty( @@ -31,22 +32,34 @@ fn new_user_aspace_empty() -> AxResult { ) } +unsafe extern "C" { + fn start_signal_trampoline(); +} + /// If the target architecture requires it, the kernel portion of the address -/// space will be copied to the user address space. -fn copy_from_kernel(aspace: &mut AddrSpace) -> AxResult { +/// space will be copied to the user address space. Signal trampoline will also +/// be mapped. +fn init_user_aspace(aspace: &mut AddrSpace) -> AxResult { if !cfg!(target_arch = "aarch64") && !cfg!(target_arch = "loongarch64") { // ARMv8 (aarch64) and LoongArch64 use separate page tables for user space // (aarch64: TTBR0_EL1, LoongArch64: PGDL), so there is no need to copy the // kernel portion to the user page table. aspace.copy_mappings_from(&kernel_aspace().lock())?; } + let signal_trampoline_paddr = virt_to_phys((start_signal_trampoline as usize).into()); + aspace.map_linear( + axconfig::plat::SIGNAL_TRAMPOLINE.into(), + signal_trampoline_paddr, + PAGE_SIZE_4K, + MappingFlags::READ | MappingFlags::EXECUTE | MappingFlags::USER, + )?; Ok(()) } fn run_user_app(args: &[String], envs: &[String]) -> Option { let mut uspace = new_user_aspace_empty() .and_then(|mut it| { - copy_from_kernel(&mut it)?; + init_user_aspace(&mut it)?; Ok(it) }) .expect("Failed to create user address space"); diff --git a/src/mm.rs b/src/mm.rs index 4c8418970..7704323c0 100644 --- a/src/mm.rs +++ b/src/mm.rs @@ -9,7 +9,7 @@ use axhal::{ }; use axmm::AddrSpace; -use axtask::TaskExtRef; +use axtask::{TaskExtMut, TaskExtRef}; use kernel_elf_parser::{AuxvEntry, ELFParser, app_stack_region}; use memory_addr::{MemoryAddr, PAGE_SIZE_4K, VirtAddr}; use xmas_elf::{ElfFile, program::SegmentData}; diff --git a/src/signal/ctypes.rs b/src/signal/ctypes.rs new file mode 100644 index 000000000..c5bcc8136 --- /dev/null +++ b/src/signal/ctypes.rs @@ -0,0 +1,167 @@ +use core::mem; + +use arceos_posix_api as api; +use axerrno::LinuxError; +use bitflags::bitflags; + +bitflags! { + #[derive(Default, Debug)] + pub struct SignalActionFlags: u32 { + const SIGINFO = 4; + const NODEFER = 0x40000000; + const RESTORER = 0x04000000; + } +} + +/// Signal set. Corresponds to `struct sigset_t` in libc. +/// +/// Currently we only support 32 standard signals. +#[derive(Default, Clone, Copy)] +#[repr(transparent)] +pub struct SignalSet { + pub bits: [u32; 2], +} +impl SignalSet { + pub fn add(&mut self, signal: u32) -> bool { + if !(1..32).contains(&signal) { + return false; + } + let bit = 1 << (signal - 1); + if self.bits[0] & bit != 0 { + return false; + } + self.bits[0] |= bit; + true + } + pub fn has(&self, signal: u32) -> bool { + (1..32).contains(&signal) && (self.bits[0] & (1 << (signal - 1))) != 0 + } + + pub fn add_from(&mut self, other: &SignalSet) { + self.bits[0] |= other.bits[0]; + self.bits[1] |= other.bits[1]; + } + pub fn remove_from(&mut self, other: &SignalSet) { + self.bits[0] &= !other.bits[0]; + self.bits[1] &= !other.bits[1]; + } + + pub fn dequeue(&mut self, mask: &SignalSet) -> Option { + let bits = self.bits[0] & !mask.bits[0]; + if bits == 0 { + None + } else { + let signal = bits.trailing_zeros(); + self.bits[0] &= !(1 << signal); + Some(signal + 1) + } + } +} + +#[derive(Clone, Copy)] +#[repr(C)] +#[allow(non_camel_case_types)] +pub struct k_sigaction { + handler: Option, + flags: u32, + restorer: Option, + mask: SignalSet, +} + +#[derive(Default)] +pub enum SignalDisposition { + #[default] + /// Use the default signal action. + Default, + /// Ignore the signal. + Ignore, + /// Custom signal handler. + Handler(unsafe extern "C" fn(i32)), +} + +/// Signal action. Corresponds to `struct sigaction` in libc. +#[derive(Default)] +pub struct SignalAction { + pub flags: SignalActionFlags, + pub mask: SignalSet, + pub disposition: SignalDisposition, + pub restorer: Option, +} +impl SignalAction { + pub fn to_ctype(&self, dest: &mut k_sigaction) { + dest.flags = self.flags.bits() as _; + dest.mask = self.mask; + match &self.disposition { + SignalDisposition::Default => { + dest.handler = None; + } + SignalDisposition::Ignore => { + // SAFETY: SIG_IGN is 1 + dest.handler = Some(unsafe { mem::transmute(1usize) }); + } + SignalDisposition::Handler(handler) => { + dest.handler = Some(*handler); + } + } + dest.restorer = self.restorer; + } +} + +impl TryFrom for SignalAction { + type Error = LinuxError; + + fn try_from(value: k_sigaction) -> Result { + let Some(flags) = SignalActionFlags::from_bits(value.flags) else { + warn!("unrecognized signal flags: {}", value.flags); + return Err(LinuxError::EINVAL); + }; + let disposition = { + match value.handler { + None => { + // SIG_DFL + SignalDisposition::Default + } + Some(h) if h as usize == 1 => { + // SIG_IGN + SignalDisposition::Ignore + } + Some(h) => { + // Custom signal handler + SignalDisposition::Handler(h) + } + } + }; + Ok(SignalAction { + flags, + mask: value.mask, + disposition, + restorer: Some( + // SAFETY: `axconfig::plat::SIGNAL_TRAMPOLINE` is a valid function pointer + value.restorer.unwrap_or_else(|| unsafe { + mem::transmute(axconfig::plat::SIGNAL_TRAMPOLINE) + }), + ), + }) + } +} + +/// Signal information. Corresponds to `struct siginfo_t` in libc. +#[derive(Default, Clone)] +#[repr(transparent)] +pub struct SignalInfo(pub api::ctypes::siginfo_t); +impl SignalInfo { + pub const SI_USER: u32 = 0; + + pub fn new(signo: u32, code: u32) -> Self { + Self(api::ctypes::siginfo_t { + si_signo: signo as _, + si_errno: 0, + si_code: code as _, + ..Default::default() + }) + } + + pub fn signo(&self) -> u32 { + self.0.si_signo as u32 + } +} diff --git a/src/signal/mod.rs b/src/signal/mod.rs new file mode 100644 index 000000000..48ee4a069 --- /dev/null +++ b/src/signal/mod.rs @@ -0,0 +1,237 @@ +pub mod ctypes; + +use core::alloc::Layout; + +use axhal::{arch::TrapFrame, trap::ANY_TRAP}; +use axtask::{TaskExtRef, current, exit}; +use ctypes::{SignalAction, SignalActionFlags, SignalDisposition, SignalInfo, SignalSet}; +use linkme::distributed_slice as register_trap_handler; + +use crate::ptr::{PtrWrapper, UserConstPtr, UserPtr}; + +pub const SIGKILL: u32 = 9; +pub const SIGSTOP: u32 = 19; + +#[derive(Debug)] +enum DefaultSignalAction { + /// Terminate the process. + Terminate, + + /// Ignore the signal. + Ignore, + + /// Terminate the process and generate a core dump. + CoreDump, + + /// Stop the process. + Stop, + + /// Continue the process if stopped. + Continue, +} +const DEFAULT_ACTIONS: [DefaultSignalAction; 32] = [ + // Unspecified + DefaultSignalAction::Ignore, + // SIGHUP + DefaultSignalAction::Terminate, + // SIGINT + DefaultSignalAction::Terminate, + // SIGQUIT + DefaultSignalAction::CoreDump, + // SIGILL + DefaultSignalAction::CoreDump, + // SIGTRAP + DefaultSignalAction::CoreDump, + // SIGABRT + DefaultSignalAction::CoreDump, + // SIGBUS + DefaultSignalAction::CoreDump, + // SIGFPE + DefaultSignalAction::CoreDump, + // SIGKILL + DefaultSignalAction::Terminate, + // SIGUSR1 + DefaultSignalAction::Terminate, + // SIGSEGV + DefaultSignalAction::CoreDump, + // SIGUSR2 + DefaultSignalAction::Terminate, + // SIGPIPE + DefaultSignalAction::Terminate, + // SIGALRM + DefaultSignalAction::Terminate, + // SIGTERM + DefaultSignalAction::Terminate, + // SIGSTKFLT + DefaultSignalAction::Terminate, + // SIGCHLD + DefaultSignalAction::Ignore, + // SIGCONT + DefaultSignalAction::Continue, + // SIGSTOP + DefaultSignalAction::Stop, + // SIGTSTP + DefaultSignalAction::Stop, + // SIGTTIN + DefaultSignalAction::Stop, + // SIGTTOU + DefaultSignalAction::Stop, + // SIGURG + DefaultSignalAction::Ignore, + // SIGXCPU + DefaultSignalAction::CoreDump, + // SIGXFSZ + DefaultSignalAction::CoreDump, + // SIGVTALRM + DefaultSignalAction::Terminate, + // SIGPROF + DefaultSignalAction::Terminate, + // SIGWINCH + DefaultSignalAction::Ignore, + // SIGIO + DefaultSignalAction::Terminate, + // SIGPWR + DefaultSignalAction::Terminate, + // SIGSYS + DefaultSignalAction::CoreDump, +]; + +/// Structure to manage signal handling in a task. +pub struct SignalManager { + /// The set of signals currently blocked from delivery. + pub blocked: SignalSet, + + /// The signal actions. + actions: [SignalAction; 32], + + /// The pending signals. + pub pending: SignalSet, + pending_info: [Option; 32], +} +impl SignalManager { + pub fn new() -> Self { + Self { + blocked: SignalSet::default(), + actions: Default::default(), + + pending: SignalSet::default(), + pending_info: Default::default(), + } + } + + pub fn send_signal(&mut self, sig: SignalInfo) -> bool { + let signo = sig.signo(); + if !self.pending.add(signo) { + return false; + } + self.pending_info[signo as usize] = Some(sig); + true + } + + pub fn set_action(&mut self, signo: u32, action: SignalAction) { + self.actions[signo as usize] = action; + } + pub fn action(&self, signo: u32) -> &SignalAction { + &self.actions[signo as usize] + } + + /// Dequeue the next pending signal. + pub fn dequeue_signal(&mut self) -> Option { + self.pending + .dequeue(&self.blocked) + .and_then(|signo| self.pending_info[signo as usize].take()) + } +} + +pub struct SignalFrame { + tf: TrapFrame, + blocked: SignalSet, + siginfo: SignalInfo, +} + +#[register_trap_handler(ANY_TRAP)] +fn handle_any_trap(tf: &mut TrapFrame, from_user: bool) -> bool { + if !from_user { + return false; + } + + let curr = current(); + let mut sigman = curr.task_ext().signal.lock(); + while let Some(sig) = sigman.dequeue_signal() { + let signo = sig.signo(); + info!("Handle signal: {}", signo); + let action = &sigman.actions[signo as usize]; + match action.disposition { + SignalDisposition::Default => match DEFAULT_ACTIONS[signo as usize] { + DefaultSignalAction::Terminate => exit(128 + signo as i32), + DefaultSignalAction::CoreDump => { + warn!("Core dump not implemented"); + exit(128 + signo as i32); + } + DefaultSignalAction::Stop => { + warn!("Stop not implemented"); + exit(-1); + } + DefaultSignalAction::Ignore => {} + DefaultSignalAction::Continue => { + warn!("Continue not implemented"); + } + }, + SignalDisposition::Ignore => {} + SignalDisposition::Handler(handler) => { + let layout = Layout::new::(); + let aligned_sp = (tf.sp() - layout.size()) & !(layout.align() - 1); + + let frame_ptr: UserPtr = aligned_sp.into(); + let frame_ptr = frame_ptr.get().expect("invalid frame ptr"); + // SAFETY: pointer is valid + let frame = unsafe { &mut *frame_ptr }; + + *frame = SignalFrame { + tf: *tf, + blocked: sigman.blocked, + siginfo: sig, + }; + + tf.set_ip(handler as _); + tf.set_sp(aligned_sp); + tf.set_arg0(signo as _); + tf.set_arg1(&frame.siginfo as *const _ as _); + tf.set_arg2(frame_ptr as _); + + let restorer = action.restorer.map_or(0, |f| f as _); + #[cfg(target_arch = "x86_64")] + tf.write_ra(restorer); + #[cfg(not(target_arch = "x86_64"))] + tf.set_ra(restorer); + + let mut mask = action.mask.clone(); + if !action.flags.contains(SignalActionFlags::NODEFER) { + mask.add(signo); + } + sigman.blocked.add_from(&mask); + } + } + } + + true +} + +pub fn sigreturn(tf: &mut TrapFrame) { + let frame_ptr: UserConstPtr = tf.sp().into(); + let frame = frame_ptr.get().expect("invalid frame ptr"); + // SAFETY: pointer is valid + let frame = unsafe { &*frame }; + + *tf = frame.tf; + #[cfg(any( + target_arch = "riscv32", + target_arch = "riscv64", + target_arch = "loongarch64" + ))] + tf.set_ip(tf.ip() - 4); + + let curr = current(); + let mut sigman = curr.task_ext().signal.lock(); + sigman.blocked = frame.blocked; +} diff --git a/src/syscall_imp/mod.rs b/src/syscall_imp/mod.rs index 28c63ddf4..12ea954b6 100644 --- a/src/syscall_imp/mod.rs +++ b/src/syscall_imp/mod.rs @@ -53,8 +53,7 @@ macro_rules! syscall_instrument {( pub(crate) use syscall_instrument; #[register_trap_handler(SYSCALL)] -fn handle_syscall(tf: &TrapFrame, syscall_num: usize) -> isize { - info!("Syscall {:?}", Sysno::from(syscall_num as u32)); +fn handle_syscall(tf: &mut TrapFrame, syscall_num: usize) -> isize { time_stat_from_user_to_kernel(); let result: LinuxResult = match Sysno::from(syscall_num as u32) { Sysno::read => sys_read(tf.arg0() as _, tf.arg1().into(), tf.arg2() as _), @@ -150,6 +149,7 @@ fn handle_syscall(tf: &TrapFrame, syscall_num: usize) -> isize { Sysno::clock_gettime => sys_clock_gettime(tf.arg0() as _, tf.arg1().into()), Sysno::exit_group => sys_exit_group(tf.arg0() as _), Sysno::getuid => sys_getuid(), + Sysno::kill => sys_kill(tf.arg0() as _, tf.arg1() as _), Sysno::rt_sigprocmask => sys_rt_sigprocmask( tf.arg0() as _, tf.arg1().into(), @@ -162,6 +162,8 @@ fn handle_syscall(tf: &TrapFrame, syscall_num: usize) -> isize { tf.arg2().into(), tf.arg3() as _, ), + Sysno::rt_sigreturn => sys_rt_sigreturn(tf), + Sysno::rt_sigpending => sys_rt_sigpending(tf.arg0().into(), tf.arg1() as _), _ => { warn!("Unimplemented syscall: {}", syscall_num); axtask::exit(LinuxError::ENOSYS as _) diff --git a/src/syscall_imp/signal.rs b/src/syscall_imp/signal.rs index a24c420d9..cc01af5d3 100644 --- a/src/syscall_imp/signal.rs +++ b/src/syscall_imp/signal.rs @@ -1,25 +1,145 @@ -use core::ffi::c_void; +use axerrno::{LinuxError, LinuxResult}; +use axhal::arch::TrapFrame; +use axtask::{TaskExtRef, current}; -use axerrno::LinuxResult; +use crate::{ + ptr::{PtrWrapper, UserConstPtr, UserPtr}, + signal::{ + ctypes::{SignalAction, SignalInfo, SignalSet, k_sigaction}, + sigreturn, + }, +}; -use crate::ptr::{UserConstPtr, UserPtr}; +fn check_sigset_size(size: usize) -> LinuxResult<()> { + if size != size_of::() { + return Err(LinuxError::EINVAL); + } + Ok(()) +} pub fn sys_rt_sigprocmask( - _how: i32, - _set: UserConstPtr, - _oldset: UserPtr, - _sigsetsize: usize, + how: i32, + set: UserConstPtr, + oldset: UserPtr, + sigsetsize: usize, ) -> LinuxResult { - warn!("sys_rt_sigprocmask: not implemented"); + check_sigset_size(sigsetsize)?; + + let curr = current(); + let mut sigman = curr.task_ext().signal.lock(); + + if let Some(oldset) = oldset.nullable(UserPtr::get)? { + // SAFETY: oldset is a valid pointer + unsafe { + oldset.write(sigman.blocked); + } + } + + if let Some(set) = set.nullable(UserConstPtr::get)? { + // SAFETY: set is a valid pointer + let set: SignalSet = unsafe { set.read() }; + match how { + // SIG_BLOCK + 0 => sigman.blocked.add_from(&set), + // SIG_UNBLOCK + 1 => sigman.blocked.remove_from(&set), + // SIG_SETMASK + 2 => sigman.blocked = set, + _ => return Err(LinuxError::EINVAL), + } + } + Ok(0) } pub fn sys_rt_sigaction( - _signum: i32, - _act: UserConstPtr, - _oldact: UserPtr, - _sigsetsize: usize, + signum: i32, + act: UserConstPtr, + oldact: UserPtr, + sigsetsize: usize, ) -> LinuxResult { - warn!("sys_rt_sigaction: not implemented"); + check_sigset_size(sigsetsize)?; + + let signum = signum as u32; + if !(1..32).contains(&signum) { + return Err(LinuxError::EINVAL); + } + if signum == crate::signal::SIGKILL || signum == crate::signal::SIGSTOP { + return Err(LinuxError::EINVAL); + } + + let curr = current(); + let mut sigman = curr.task_ext().signal.lock(); + + if let Some(oldact) = oldact.nullable(UserPtr::get)? { + // SAFETY: oldact is a valid pointer + sigman.action(signum).to_ctype(unsafe { &mut *oldact }); + } + + if let Some(act) = act.nullable(UserConstPtr::get)? { + // SAFETY: act is a valid pointer + let action: SignalAction = unsafe { act.read() }.try_into()?; + sigman.set_action(signum, action); + } + Ok(0) } + +pub fn sys_rt_sigpending(set: UserPtr, sigsetsize: usize) -> LinuxResult { + check_sigset_size(sigsetsize)?; + + let curr = current(); + let sigman = curr.task_ext().signal.lock(); + + let set = set.get()?; + // SAFETY: set is a valid pointer + unsafe { set.write(sigman.pending) }; + + Ok(0) +} + +pub fn sys_rt_sigreturn(tf: &mut TrapFrame) -> LinuxResult { + sigreturn(tf); + Ok(tf.retval() as _) +} + +pub fn sys_kill(pid: i32, sig: i32) -> LinuxResult { + if !(1..32).contains(&sig) { + return Err(LinuxError::EINVAL); + } + if sig == 0 { + // TODO: should also check permissions + return Ok(0); + } + + let sig = SignalInfo::new(sig as u32, SignalInfo::SI_USER); + + let curr = current(); + let mut result = 0; + match pid { + 1.. => { + warn!("killing specified pid is not implemented"); + return Err(LinuxError::ENOSYS); + } + 0 => { + // TODO: this should send signal to the current process group + result += curr.task_ext().signal.lock().send_signal(sig) as isize; + } + -1 => { + // TODO: this should "send signal to every process for which the + // calling process has permission to send signals, except for + // process 1" + let mut guard = curr.task_ext().signal.lock(); + for child in curr.task_ext().children.lock().iter() { + result += child.task_ext().signal.lock().send_signal(sig.clone()) as isize; + } + result += guard.send_signal(sig) as isize; + } + ..-1 => { + warn!("killing specified process group is not implemented"); + return Err(LinuxError::ENOSYS); + } + } + + Ok(result) +} diff --git a/src/syscall_imp/task/thread.rs b/src/syscall_imp/task/thread.rs index 875a7a58c..d72fd187a 100644 --- a/src/syscall_imp/task/thread.rs +++ b/src/syscall_imp/task/thread.rs @@ -119,6 +119,10 @@ pub fn sys_clone( arg3: usize, arg4: usize, ) -> LinuxResult { + info!( + "sys_clone: flags: {:x}, user_stack: {:x}, ptid: {:x}, arg3: {:x}, arg4: {:x}", + flags, user_stack, ptid, arg3, arg4 + ); let tls = arg3; let ctid = arg4; diff --git a/src/task.rs b/src/task.rs index 03c827b94..e2b8d7920 100644 --- a/src/task.rs +++ b/src/task.rs @@ -15,8 +15,9 @@ use memory_addr::VirtAddrRange; use spin::Once; use crate::{ - copy_from_kernel, + init_user_aspace, ctypes::{CloneFlags, TimeStat, WaitStatus}, + signal::SignalManager, }; use axhal::{ arch::{TrapFrame, UspaceContext}, @@ -24,7 +25,7 @@ use axhal::{ }; use axmm::{AddrSpace, kernel_aspace}; use axns::{AxNamespace, AxNamespaceIf}; -use axsync::Mutex; +use axsync::{Mutex, spin::SpinNoIrq}; use axtask::{AxTaskRef, TaskExtRef, TaskInner, current}; /// Task extended data for the monolithic kernel. @@ -53,6 +54,8 @@ pub struct TaskExt { pub heap_bottom: AtomicU64, /// The user heap top pub heap_top: AtomicU64, + // The signal manager + pub signal: SpinNoIrq, } impl TaskExt { @@ -73,6 +76,7 @@ impl TaskExt { time: TimeStat::new().into(), heap_bottom: AtomicU64::new(heap_bottom), heap_top: AtomicU64::new(heap_bottom), + signal: SpinNoIrq::new(SignalManager::new()), } } @@ -92,8 +96,8 @@ impl TaskExt { let kstack_top = curr.kernel_stack_top().unwrap(); info!( "Enter user space: entry={:#x}, ustack={:#x}, kstack={:#x}", - curr.task_ext().uctx.get_ip(), - curr.task_ext().uctx.get_sp(), + curr.task_ext().uctx.trap_frame().ip(), + curr.task_ext().uctx.trap_frame().sp(), kstack_top, ); unsafe { curr.task_ext().uctx.enter_uspace(kstack_top) }; @@ -105,20 +109,21 @@ impl TaskExt { let current_task = current(); let mut current_aspace = current_task.task_ext().aspace.lock(); let mut new_aspace = current_aspace.clone_or_err()?; - copy_from_kernel(&mut new_aspace)?; + init_user_aspace(&mut new_aspace)?; new_task .ctx_mut() .set_page_table_root(new_aspace.page_table_root()); - let trap_frame = read_trapframe_from_kstack(current_task.get_kernel_stack_top().unwrap()); - let mut new_uctx = UspaceContext::from(&trap_frame); + let mut trap_frame = + read_trapframe_from_kstack(current_task.get_kernel_stack_top().unwrap()); if let Some(stack) = stack { - new_uctx.set_sp(stack); + trap_frame.set_sp(stack); } // Skip current instruction #[cfg(any(target_arch = "riscv64", target_arch = "loongarch64"))] - new_uctx.set_ip(new_uctx.get_ip() + 4); - new_uctx.set_retval(0); + trap_frame.set_ip(trap_frame.ip() + 4); + trap_frame.set_retval(0); + let new_uctx = UspaceContext::from(&trap_frame); let return_id: u64 = new_task.id().as_u64(); let new_task_ext = TaskExt::new( return_id as usize, @@ -249,8 +254,8 @@ pub fn spawn_user_task( let kstack_top = curr.kernel_stack_top().unwrap(); info!( "Enter user space: entry={:#x}, ustack={:#x}, kstack={:#x}", - curr.task_ext().uctx.get_ip(), - curr.task_ext().uctx.get_sp(), + curr.task_ext().uctx.trap_frame().ip(), + curr.task_ext().uctx.trap_frame().sp(), kstack_top, ); unsafe { curr.task_ext().uctx.enter_uspace(kstack_top) }; From d0679c6b7d12e9c5758eb9cdec62f2c8efb4ff4c Mon Sep 17 00:00:00 2001 From: mivik Date: Thu, 27 Mar 2025 00:34:21 +0800 Subject: [PATCH 02/16] feat: add fork support for x86_64 --- src/syscall_imp/mod.rs | 2 ++ src/task.rs | 4 ++++ 2 files changed, 6 insertions(+) diff --git a/src/syscall_imp/mod.rs b/src/syscall_imp/mod.rs index 12ea954b6..9254de277 100644 --- a/src/syscall_imp/mod.rs +++ b/src/syscall_imp/mod.rs @@ -85,6 +85,8 @@ fn handle_syscall(tf: &mut TrapFrame, syscall_num: usize) -> isize { tf.arg3() as _, tf.arg4() as _, ), + #[cfg(target_arch = "x86_64")] + Sysno::fork => sys_clone(17 /* SIGCHLD */, 0, 0, 0, 0), Sysno::wait4 => sys_wait4(tf.arg0() as _, tf.arg1().into(), tf.arg2() as _), Sysno::pipe2 => sys_pipe2(tf.arg0().into()), Sysno::close => sys_close(tf.arg0() as _), diff --git a/src/task.rs b/src/task.rs index e2b8d7920..951bc9bdc 100644 --- a/src/task.rs +++ b/src/task.rs @@ -105,6 +105,10 @@ impl TaskExt { current().id_name(), axconfig::plat::KERNEL_STACK_SIZE, ); + #[cfg(target_arch = "x86_64")] + unsafe { + new_task.ctx_mut().set_tls(axhal::arch::read_thread_pointer().into()); + } let current_task = current(); let mut current_aspace = current_task.task_ext().aspace.lock(); From 043b4e5f29dbc63f0fb2981fdc5166245c8fccce Mon Sep 17 00:00:00 2001 From: mivik Date: Thu, 27 Mar 2025 00:38:34 +0800 Subject: [PATCH 03/16] style: fix clippy --- src/mm.rs | 2 +- src/signal/ctypes.rs | 3 ++- src/signal/mod.rs | 2 +- 3 files changed, 4 insertions(+), 3 deletions(-) diff --git a/src/mm.rs b/src/mm.rs index 7704323c0..4c8418970 100644 --- a/src/mm.rs +++ b/src/mm.rs @@ -9,7 +9,7 @@ use axhal::{ }; use axmm::AddrSpace; -use axtask::{TaskExtMut, TaskExtRef}; +use axtask::TaskExtRef; use kernel_elf_parser::{AuxvEntry, ELFParser, app_stack_region}; use memory_addr::{MemoryAddr, PAGE_SIZE_4K, VirtAddr}; use xmas_elf::{ElfFile, program::SegmentData}; diff --git a/src/signal/ctypes.rs b/src/signal/ctypes.rs index c5bcc8136..df560ed81 100644 --- a/src/signal/ctypes.rs +++ b/src/signal/ctypes.rs @@ -97,7 +97,8 @@ impl SignalAction { } SignalDisposition::Ignore => { // SAFETY: SIG_IGN is 1 - dest.handler = Some(unsafe { mem::transmute(1usize) }); + dest.handler = + Some(unsafe { mem::transmute::(1) }); } SignalDisposition::Handler(handler) => { dest.handler = Some(*handler); diff --git a/src/signal/mod.rs b/src/signal/mod.rs index 48ee4a069..aa68777ed 100644 --- a/src/signal/mod.rs +++ b/src/signal/mod.rs @@ -205,7 +205,7 @@ fn handle_any_trap(tf: &mut TrapFrame, from_user: bool) -> bool { #[cfg(not(target_arch = "x86_64"))] tf.set_ra(restorer); - let mut mask = action.mask.clone(); + let mut mask = action.mask; if !action.flags.contains(SignalActionFlags::NODEFER) { mask.add(signo); } From 329bb97a008d16878db1056bb781514acfceffd9 Mon Sep 17 00:00:00 2001 From: mivik Date: Thu, 27 Mar 2025 00:38:45 +0800 Subject: [PATCH 04/16] style: fmt --- src/task.rs | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/src/task.rs b/src/task.rs index 951bc9bdc..a502ad579 100644 --- a/src/task.rs +++ b/src/task.rs @@ -15,8 +15,8 @@ use memory_addr::VirtAddrRange; use spin::Once; use crate::{ - init_user_aspace, ctypes::{CloneFlags, TimeStat, WaitStatus}, + init_user_aspace, signal::SignalManager, }; use axhal::{ @@ -107,7 +107,9 @@ impl TaskExt { ); #[cfg(target_arch = "x86_64")] unsafe { - new_task.ctx_mut().set_tls(axhal::arch::read_thread_pointer().into()); + new_task + .ctx_mut() + .set_tls(axhal::arch::read_thread_pointer().into()); } let current_task = current(); From 20e25130610a3d260be94344d02bd208ccdbe607 Mon Sep 17 00:00:00 2001 From: mivik Date: Thu, 27 Mar 2025 10:34:07 +0800 Subject: [PATCH 05/16] fix: update any trap handling --- src/signal/mod.rs | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/src/signal/mod.rs b/src/signal/mod.rs index aa68777ed..948de396c 100644 --- a/src/signal/mod.rs +++ b/src/signal/mod.rs @@ -150,9 +150,9 @@ pub struct SignalFrame { } #[register_trap_handler(ANY_TRAP)] -fn handle_any_trap(tf: &mut TrapFrame, from_user: bool) -> bool { +fn handle_any_trap(tf: &mut TrapFrame, from_user: bool) { if !from_user { - return false; + return; } let curr = current(); @@ -213,8 +213,6 @@ fn handle_any_trap(tf: &mut TrapFrame, from_user: bool) -> bool { } } } - - true } pub fn sigreturn(tf: &mut TrapFrame) { From 97388d6896017ff022d4580b4d1d9dbf6793cf33 Mon Sep 17 00:00:00 2001 From: mivik Date: Thu, 27 Mar 2025 10:34:21 +0800 Subject: [PATCH 06/16] style: remove x86_64 unused unsafe --- src/task.rs | 8 +++----- 1 file changed, 3 insertions(+), 5 deletions(-) diff --git a/src/task.rs b/src/task.rs index a502ad579..dc0b02cb9 100644 --- a/src/task.rs +++ b/src/task.rs @@ -106,11 +106,9 @@ impl TaskExt { axconfig::plat::KERNEL_STACK_SIZE, ); #[cfg(target_arch = "x86_64")] - unsafe { - new_task - .ctx_mut() - .set_tls(axhal::arch::read_thread_pointer().into()); - } + new_task + .ctx_mut() + .set_tls(axhal::arch::read_thread_pointer().into()); let current_task = current(); let mut current_aspace = current_task.task_ext().aspace.lock(); From 1a01b148eca1fa962aa3fa627fb9267c71b4f618 Mon Sep 17 00:00:00 2001 From: mivik Date: Thu, 27 Mar 2025 15:34:59 +0800 Subject: [PATCH 07/16] fix: only map signal trampoline on init --- src/main.rs | 21 ++++++++++----------- src/task.rs | 4 ++-- 2 files changed, 12 insertions(+), 13 deletions(-) diff --git a/src/main.rs b/src/main.rs index f2109768c..081473013 100644 --- a/src/main.rs +++ b/src/main.rs @@ -37,29 +37,28 @@ unsafe extern "C" { } /// If the target architecture requires it, the kernel portion of the address -/// space will be copied to the user address space. Signal trampoline will also -/// be mapped. -fn init_user_aspace(aspace: &mut AddrSpace) -> AxResult { +/// space will be copied to the user address space. +fn copy_from_kernel(aspace: &mut AddrSpace) -> AxResult { if !cfg!(target_arch = "aarch64") && !cfg!(target_arch = "loongarch64") { // ARMv8 (aarch64) and LoongArch64 use separate page tables for user space // (aarch64: TTBR0_EL1, LoongArch64: PGDL), so there is no need to copy the // kernel portion to the user page table. aspace.copy_mappings_from(&kernel_aspace().lock())?; } - let signal_trampoline_paddr = virt_to_phys((start_signal_trampoline as usize).into()); - aspace.map_linear( - axconfig::plat::SIGNAL_TRAMPOLINE.into(), - signal_trampoline_paddr, - PAGE_SIZE_4K, - MappingFlags::READ | MappingFlags::EXECUTE | MappingFlags::USER, - )?; Ok(()) } fn run_user_app(args: &[String], envs: &[String]) -> Option { let mut uspace = new_user_aspace_empty() .and_then(|mut it| { - init_user_aspace(&mut it)?; + copy_from_kernel(&mut it)?; + let signal_trampoline_paddr = virt_to_phys((start_signal_trampoline as usize).into()); + it.map_linear( + axconfig::plat::SIGNAL_TRAMPOLINE.into(), + signal_trampoline_paddr, + PAGE_SIZE_4K, + MappingFlags::READ | MappingFlags::EXECUTE | MappingFlags::USER, + )?; Ok(it) }) .expect("Failed to create user address space"); diff --git a/src/task.rs b/src/task.rs index dc0b02cb9..64a650f67 100644 --- a/src/task.rs +++ b/src/task.rs @@ -16,7 +16,7 @@ use spin::Once; use crate::{ ctypes::{CloneFlags, TimeStat, WaitStatus}, - init_user_aspace, + copy_from_kernel, signal::SignalManager, }; use axhal::{ @@ -113,7 +113,7 @@ impl TaskExt { let current_task = current(); let mut current_aspace = current_task.task_ext().aspace.lock(); let mut new_aspace = current_aspace.clone_or_err()?; - init_user_aspace(&mut new_aspace)?; + copy_from_kernel(&mut new_aspace)?; new_task .ctx_mut() .set_page_table_root(new_aspace.page_table_root()); From 16cf98337ea9164ce899ba193b6a5012f534a5be Mon Sep 17 00:00:00 2001 From: mivik Date: Thu, 27 Mar 2025 15:35:37 +0800 Subject: [PATCH 08/16] style: fmt --- src/task.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/task.rs b/src/task.rs index 64a650f67..01e2e0403 100644 --- a/src/task.rs +++ b/src/task.rs @@ -15,8 +15,8 @@ use memory_addr::VirtAddrRange; use spin::Once; use crate::{ - ctypes::{CloneFlags, TimeStat, WaitStatus}, copy_from_kernel, + ctypes::{CloneFlags, TimeStat, WaitStatus}, signal::SignalManager, }; use axhal::{ From 4740c799c0961bf97871f55000983695112053e5 Mon Sep 17 00:00:00 2001 From: mivik Date: Thu, 27 Mar 2025 18:32:16 +0800 Subject: [PATCH 09/16] fix: sync with arceos --- src/signal/mod.rs | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/signal/mod.rs b/src/signal/mod.rs index 948de396c..e8f742bb4 100644 --- a/src/signal/mod.rs +++ b/src/signal/mod.rs @@ -2,7 +2,7 @@ pub mod ctypes; use core::alloc::Layout; -use axhal::{arch::TrapFrame, trap::ANY_TRAP}; +use axhal::{arch::TrapFrame, trap::POST_TRAP}; use axtask::{TaskExtRef, current, exit}; use ctypes::{SignalAction, SignalActionFlags, SignalDisposition, SignalInfo, SignalSet}; use linkme::distributed_slice as register_trap_handler; @@ -149,8 +149,8 @@ pub struct SignalFrame { siginfo: SignalInfo, } -#[register_trap_handler(ANY_TRAP)] -fn handle_any_trap(tf: &mut TrapFrame, from_user: bool) { +#[register_trap_handler(POST_TRAP)] +fn handle_post_trap(tf: &mut TrapFrame, from_user: bool) { if !from_user { return; } From 778884a2a0590665f9165a28039f3a10c1d585b1 Mon Sep 17 00:00:00 2001 From: mivik Date: Thu, 27 Mar 2025 18:59:47 +0800 Subject: [PATCH 10/16] fix(test): signal test --- apps/libc/c/signal/signal.c | 27 +++++++++++---------------- apps/libc/expect_off.out | 17 ++++++----------- 2 files changed, 17 insertions(+), 27 deletions(-) diff --git a/apps/libc/c/signal/signal.c b/apps/libc/c/signal/signal.c index c68d71159..71a85c36b 100644 --- a/apps/libc/c/signal/signal.c +++ b/apps/libc/c/signal/signal.c @@ -4,13 +4,12 @@ #include void test_term() { - puts("test_term"); if (fork() == 0) { - kill(0, SIGTERM); + kill(getpid(), SIGTERM); puts("This should not be printed"); } wait(0); - puts("Done"); + puts("test_term ok"); } static void signal_handler(int signum) { @@ -26,25 +25,23 @@ static void signal_handler(int signum) { } void test_sigaction() { - puts("test_sigaction"); struct sigaction sa = {0}; sa.sa_handler = signal_handler; sa.sa_flags = 0; sigaction(SIGTERM, &sa, NULL); kill(0, SIGTERM); - puts("Ok1"); + puts("test_sigaction ok1"); sa.sa_handler = (void (*)(int))1; sigaction(SIGTERM, &sa, NULL); kill(0, SIGTERM); - puts("Ok2"); + puts("test_sigaction ok2"); sa.sa_handler = (void (*)(int))0; sigaction(SIGTERM, &sa, NULL); } void test_sigprocmask() { - puts("test_sigprocmask"); sigset_t set, set2; sigemptyset(&set); sigaddset(&set, SIGTERM); @@ -53,7 +50,7 @@ void test_sigprocmask() { sigpending(&set2); if (sigismember(&set2, SIGTERM)) { - puts("Ok1"); + puts("test_sigprocmask ok1"); } // Ignore SIGTERM for once @@ -67,7 +64,7 @@ void test_sigprocmask() { sigpending(&set2); if (!sigismember(&set2, SIGTERM)) { - puts("Ok2"); + puts("test_sigprocmask ok2"); } sa.sa_handler = (void (*)(int))0; @@ -75,21 +72,19 @@ void test_sigprocmask() { } void test_sigkill_stop() { - puts("test_sigkill_stop"); struct sigaction sa = {0}; sa.sa_handler = signal_handler; sa.sa_flags = 0; - if (sigaction(SIGKILL, &sa, NULL) == 0) { - puts("Wrong SIGKILL"); + if (sigaction(SIGKILL, &sa, NULL) < 0) { + puts("test_sigkill_stop ok1"); } - if (sigaction(SIGSTOP, &sa, NULL) == 0) { - puts("Wrong SIGSTOP"); + if (sigaction(SIGSTOP, &sa, NULL) < 0) { + puts("test_sigkill_stop ok2"); } - puts("Done"); } int main() { - test_term(); + // test_term(); test_sigaction(); test_sigprocmask(); test_sigkill_stop(); diff --git a/apps/libc/expect_off.out b/apps/libc/expect_off.out index 7fedadbfd..53c1fbee5 100644 --- a/apps/libc/expect_off.out +++ b/apps/libc/expect_off.out @@ -6,17 +6,12 @@ Hello, World! Sleeping for 2 seconds... Done! -test_term -Done -test_sigaction Received signal 15, count=1 End, count=1 Received signal 15, count=2 -Ok1 -Ok2 -test_sigprocmask -Ok1 -Ok2 -Done -test_sigkill_stop -Done +test_sigaction ok1 +test_sigaction ok2 +test_sigprocmask ok1 +test_sigprocmask ok2 +test_sigkill_stop ok1 +test_sigkill_stop ok2 From 7f64915f72d7c32af345f3c5c6f47ba7f7a3e72f Mon Sep 17 00:00:00 2001 From: mivik Date: Sun, 30 Mar 2025 23:33:03 +0800 Subject: [PATCH 11/16] feat(signal): implement rt_sigtimedwait & rt_sigsuspend --- apps/libc/c/signal/signal.c | 95 +++++++++++++++++++++++++++- apps/libc/expect_off.out | 7 +++ src/signal/ctypes.rs | 27 +++++++- src/signal/mod.rs | 91 ++++++++++++++++++++------- src/syscall_imp/mod.rs | 7 +++ src/syscall_imp/signal.rs | 121 +++++++++++++++++++++++++++++++++++- 6 files changed, 318 insertions(+), 30 deletions(-) diff --git a/apps/libc/c/signal/signal.c b/apps/libc/c/signal/signal.c index 71a85c36b..9d0b65105 100644 --- a/apps/libc/c/signal/signal.c +++ b/apps/libc/c/signal/signal.c @@ -1,14 +1,17 @@ +#include #include #include +#include #include #include void test_term() { if (fork() == 0) { kill(getpid(), SIGTERM); - puts("This should not be printed"); + while (1) + ; } - wait(0); + wait(NULL); puts("test_term ok"); } @@ -83,10 +86,96 @@ void test_sigkill_stop() { } } +void test_sigwait() { + int pid = fork(); + if (pid == 0) { + sigset_t set; + sigemptyset(&set); + sigaddset(&set, SIGTERM); + sigprocmask(SIG_BLOCK, &set, NULL); + int sig; + sigwait(&set, &sig); + if (sig == SIGTERM) { + puts("test_sigwait ok1"); + } + exit(0); + } + sleep(1); + kill(pid, SIGTERM); + wait(NULL); + puts("test_sigwait ok2"); + + sigset_t set; + sigemptyset(&set); + sigaddset(&set, SIGTERM); + sigprocmask(SIG_BLOCK, &set, NULL); + struct timespec ts = {1, 0}; + if (sigtimedwait(&set, NULL, &ts) < 0 && errno == EAGAIN) { + puts("test_sigwait ok3"); + } + sigprocmask(SIG_UNBLOCK, &set, NULL); +} + +static void signal_handler2(int signum) { + puts("test_sigsuspend ok1"); +} +static void signal_handler3(int signum) { + puts("test_sigsuspend ok3"); +} +void test_sigsuspend() { + int pid = fork(); + if (pid == 0) { + struct sigaction sa = {0}; + sa.sa_handler = signal_handler2; + sa.sa_flags = 0; + sigaction(SIGUSR1, &sa, NULL); + + sigset_t set; + sigemptyset(&set); + sigaddset(&set, SIGTERM); + sigsuspend(&set); + // SIGTERM is handled immediately after so this won't run + // To ensure it, we check this return code below + exit(0); + } + sleep(1); + kill(pid, SIGTERM); + sleep(1); + kill(pid, SIGUSR1); + int status; + wait(&status); + if (status != 0) { + puts("test_sigsuspend ok2"); + } + + pid = fork(); + if (pid == 0) { + // Ignore SIGTERM + struct sigaction sa = {0}; + sa.sa_handler = (void (*)(int))1; + sa.sa_flags = 0; + sigaction(SIGTERM, &sa, NULL); + + sa.sa_handler = signal_handler3; + sigaction(SIGUSR1, &sa, NULL); + + sigset_t set; + sigemptyset(&set); + sigsuspend(&set); + exit(0); + } + sleep(1); + kill(pid, SIGTERM); // SIGTERM is ignored so sigsuspend won't unblock + sleep(1); + kill(pid, SIGUSR1); +} + int main() { - // test_term(); + test_term(); test_sigaction(); test_sigprocmask(); test_sigkill_stop(); + test_sigwait(); + test_sigsuspend(); return 0; } \ No newline at end of file diff --git a/apps/libc/expect_off.out b/apps/libc/expect_off.out index 53c1fbee5..4e86961f3 100644 --- a/apps/libc/expect_off.out +++ b/apps/libc/expect_off.out @@ -6,6 +6,7 @@ Hello, World! Sleeping for 2 seconds... Done! +test_term ok Received signal 15, count=1 End, count=1 Received signal 15, count=2 @@ -15,3 +16,9 @@ test_sigprocmask ok1 test_sigprocmask ok2 test_sigkill_stop ok1 test_sigkill_stop ok2 +test_sigwait ok1 +test_sigwait ok2 +test_sigwait ok3 +test_sigsuspend ok1 +test_sigsuspend ok2 +test_sigsuspend ok3 diff --git a/src/signal/ctypes.rs b/src/signal/ctypes.rs index df560ed81..4c975a911 100644 --- a/src/signal/ctypes.rs +++ b/src/signal/ctypes.rs @@ -1,4 +1,4 @@ -use core::mem; +use core::{mem, ops::Not}; use arceos_posix_api as api; use axerrno::LinuxError; @@ -33,6 +33,18 @@ impl SignalSet { self.bits[0] |= bit; true } + pub fn remove(&mut self, signal: u32) -> bool { + if !(1..32).contains(&signal) { + return false; + } + let bit = 1 << (signal - 1); + if self.bits[0] & bit == 0 { + return false; + } + self.bits[0] &= !bit; + true + } + pub fn has(&self, signal: u32) -> bool { (1..32).contains(&signal) && (self.bits[0] & (1 << (signal - 1))) != 0 } @@ -46,8 +58,9 @@ impl SignalSet { self.bits[1] &= !other.bits[1]; } + /// Dequeue the a signal in `mask` from this set, if any. pub fn dequeue(&mut self, mask: &SignalSet) -> Option { - let bits = self.bits[0] & !mask.bits[0]; + let bits = self.bits[0] & mask.bits[0]; if bits == 0 { None } else { @@ -58,6 +71,16 @@ impl SignalSet { } } +impl Not for SignalSet { + type Output = Self; + + fn not(self) -> Self::Output { + Self { + bits: [!self.bits[0], !self.bits[1]], + } + } +} + #[derive(Clone, Copy)] #[repr(C)] #[allow(non_camel_case_types)] diff --git a/src/signal/mod.rs b/src/signal/mod.rs index e8f742bb4..ebf7d661d 100644 --- a/src/signal/mod.rs +++ b/src/signal/mod.rs @@ -2,8 +2,9 @@ pub mod ctypes; use core::alloc::Layout; +use alloc::sync::Arc; use axhal::{arch::TrapFrame, trap::POST_TRAP}; -use axtask::{TaskExtRef, current, exit}; +use axtask::{TaskExtRef, WaitQueue, current, exit}; use ctypes::{SignalAction, SignalActionFlags, SignalDisposition, SignalInfo, SignalSet}; use linkme::distributed_slice as register_trap_handler; @@ -105,8 +106,23 @@ pub struct SignalManager { actions: [SignalAction; 32], /// The pending signals. + /// + /// Note that does not correspond to `pending signals` as described in + /// Linux. `Pending signals` in Linux refers to the signals that are + /// delivered but blocked from delivery, while `pending` here refers to any + /// signal that is delivered and not yet handled. pub pending: SignalSet, pending_info: [Option; 32], + + /// The signals the task is currently waiting for. Used by + /// `sys_rt_sigtimedwait`. + pub waiting: SignalSet, + pub wq: Arc, + + /// If this is true, no more custom signal handler function should be + /// written to the trap frame, until it is set back to false at the end of + /// [`post_trap_callback`]. + pub prevent_signal_handling: bool, } impl SignalManager { pub fn new() -> Self { @@ -116,6 +132,11 @@ impl SignalManager { pending: SignalSet::default(), pending_info: Default::default(), + + waiting: SignalSet::default(), + wq: Arc::new(WaitQueue::new()), + + prevent_signal_handling: false, } } @@ -125,6 +146,9 @@ impl SignalManager { return false; } self.pending_info[signo as usize] = Some(sig); + if self.waiting.has(signo) { + self.wq.notify_one(false); + } true } @@ -135,32 +159,26 @@ impl SignalManager { &self.actions[signo as usize] } - /// Dequeue the next pending signal. - pub fn dequeue_signal(&mut self) -> Option { + /// Dequeue the next pending signal contained in `mask`, if any. + pub fn dequeue_signal_in(&mut self, mask: &SignalSet) -> Option { self.pending - .dequeue(&self.blocked) + .dequeue(mask) .and_then(|signo| self.pending_info[signo as usize].take()) } -} - -pub struct SignalFrame { - tf: TrapFrame, - blocked: SignalSet, - siginfo: SignalInfo, -} -#[register_trap_handler(POST_TRAP)] -fn handle_post_trap(tf: &mut TrapFrame, from_user: bool) { - if !from_user { - return; + /// Dequeue the next non-blocked pending signal. + pub fn dequeue_signal(&mut self) -> Option { + self.dequeue_signal_in(&!self.blocked) } - let curr = current(); - let mut sigman = curr.task_ext().signal.lock(); - while let Some(sig) = sigman.dequeue_signal() { + /// Run the signal handler for the given signal. + /// + /// Returns `true` if the process should be terminated or a signal handler + /// should be executed. + pub fn run_action(&mut self, tf: &mut TrapFrame, sig: SignalInfo) -> bool { let signo = sig.signo(); info!("Handle signal: {}", signo); - let action = &sigman.actions[signo as usize]; + let action = &self.actions[signo as usize]; match action.disposition { SignalDisposition::Default => match DEFAULT_ACTIONS[signo as usize] { DefaultSignalAction::Terminate => exit(128 + signo as i32), @@ -172,12 +190,13 @@ fn handle_post_trap(tf: &mut TrapFrame, from_user: bool) { warn!("Stop not implemented"); exit(-1); } - DefaultSignalAction::Ignore => {} + DefaultSignalAction::Ignore => false, DefaultSignalAction::Continue => { warn!("Continue not implemented"); + true } }, - SignalDisposition::Ignore => {} + SignalDisposition::Ignore => false, SignalDisposition::Handler(handler) => { let layout = Layout::new::(); let aligned_sp = (tf.sp() - layout.size()) & !(layout.align() - 1); @@ -189,7 +208,7 @@ fn handle_post_trap(tf: &mut TrapFrame, from_user: bool) { *frame = SignalFrame { tf: *tf, - blocked: sigman.blocked, + blocked: self.blocked, siginfo: sig, }; @@ -209,12 +228,38 @@ fn handle_post_trap(tf: &mut TrapFrame, from_user: bool) { if !action.flags.contains(SignalActionFlags::NODEFER) { mask.add(signo); } - sigman.blocked.add_from(&mask); + self.blocked.add_from(&mask); + true } } } } +pub struct SignalFrame { + tf: TrapFrame, + blocked: SignalSet, + siginfo: SignalInfo, +} + +#[register_trap_handler(POST_TRAP)] +fn post_trap_callback(tf: &mut TrapFrame, from_user: bool) { + if !from_user { + return; + } + + let curr = current(); + let mut sigman = curr.task_ext().signal.lock(); + if sigman.prevent_signal_handling { + sigman.prevent_signal_handling = false; + return; + } + while let Some(sig) = sigman.dequeue_signal() { + if sigman.run_action(tf, sig) { + break; + } + } +} + pub fn sigreturn(tf: &mut TrapFrame) { let frame_ptr: UserConstPtr = tf.sp().into(); let frame = frame_ptr.get().expect("invalid frame ptr"); diff --git a/src/syscall_imp/mod.rs b/src/syscall_imp/mod.rs index 9254de277..d4e8e1046 100644 --- a/src/syscall_imp/mod.rs +++ b/src/syscall_imp/mod.rs @@ -166,6 +166,13 @@ fn handle_syscall(tf: &mut TrapFrame, syscall_num: usize) -> isize { ), Sysno::rt_sigreturn => sys_rt_sigreturn(tf), Sysno::rt_sigpending => sys_rt_sigpending(tf.arg0().into(), tf.arg1() as _), + Sysno::rt_sigtimedwait => sys_rt_sigtimedwait( + tf.arg0().into(), + tf.arg1().into(), + tf.arg2().into(), + tf.arg3() as _, + ), + Sysno::rt_sigsuspend => sys_rt_sigsuspend(tf, tf.arg0().into(), tf.arg1() as _), _ => { warn!("Unimplemented syscall: {}", syscall_num); axtask::exit(LinuxError::ENOSYS as _) diff --git a/src/syscall_imp/signal.rs b/src/syscall_imp/signal.rs index cc01af5d3..43152eeb9 100644 --- a/src/syscall_imp/signal.rs +++ b/src/syscall_imp/signal.rs @@ -1,3 +1,6 @@ +use core::time::Duration; + +use arceos_posix_api as api; use axerrno::{LinuxError, LinuxResult}; use axhal::arch::TrapFrame; use axtask::{TaskExtRef, current}; @@ -5,6 +8,7 @@ use axtask::{TaskExtRef, current}; use crate::{ ptr::{PtrWrapper, UserConstPtr, UserPtr}, signal::{ + SIGKILL, SIGSTOP, ctypes::{SignalAction, SignalInfo, SignalSet, k_sigaction}, sigreturn, }, @@ -103,6 +107,109 @@ pub fn sys_rt_sigreturn(tf: &mut TrapFrame) -> LinuxResult { Ok(tf.retval() as _) } +pub fn sys_rt_sigtimedwait( + set: UserConstPtr, + info: UserPtr, + timeout: UserConstPtr, + sigsetsize: usize, +) -> LinuxResult { + check_sigset_size(sigsetsize)?; + + let set = set.get()?; + // SAFETY: set is a valid pointer + let mut set = SignalSet::from(unsafe { set.read() }); + + let timeout = timeout.nullable(UserConstPtr::get)?.map(|spec| { + // SAFETY: spec is a valid pointer + let spec = unsafe { spec.read() }; + Duration::new(spec.tv_sec as u64, spec.tv_nsec as u32) + }); + + let curr = current(); + let mut sigman = curr.task_ext().signal.lock(); + + // Non-blocked signals cannot be waited + set.remove_from(&!sigman.blocked); + + if let Some(siginfo) = sigman.dequeue_signal_in(&set) { + if let Some(info) = info.nullable(UserPtr::get)? { + // SAFETY: info is a valid pointer + unsafe { info.write(siginfo) }; + } + return Ok(0); + } + + sigman.waiting = set; + let wq = sigman.wq.clone(); + drop(sigman); + + if let Some(timeout) = timeout { + wq.wait_timeout(timeout); + } else { + wq.wait(); + } + + let mut sigman = curr.task_ext().signal.lock(); + if let Some(siginfo) = sigman.dequeue_signal_in(&set) { + if let Some(info) = info.nullable(UserPtr::get)? { + // SAFETY: info is a valid pointer + unsafe { info.write(siginfo) }; + } + return Ok(0); + } + + // TODO: EINTR + + Err(LinuxError::EAGAIN) +} + +pub fn sys_rt_sigsuspend( + tf: &mut TrapFrame, + set: UserPtr, + sigsetsize: usize, +) -> LinuxResult { + check_sigset_size(sigsetsize)?; + + let curr = current(); + let set = set.get()?; + // SAFETY: set is a valid pointer + let mut set = unsafe { *set }; + + set.remove(SIGKILL); + set.remove(SIGSTOP); + + let mut sigman = curr.task_ext().signal.lock(); + let old_blocked = sigman.blocked; + + sigman.blocked = set; + sigman.waiting = !set; + drop(sigman); + + // TODO: document this + #[cfg(any( + target_arch = "riscv32", + target_arch = "riscv64", + target_arch = "loongarch64" + ))] + tf.set_ip(tf.ip() + 4); + tf.set_retval((-LinuxError::EINTR.code() as isize) as usize); + + 'wait: loop { + let mut sigman = curr.task_ext().signal.lock(); + while let Some(sig) = sigman.dequeue_signal() { + if sigman.run_action(tf, sig) { + sigman.blocked = old_blocked; + sigman.prevent_signal_handling = true; + break 'wait; + } + } + let wq = sigman.wq.clone(); + drop(sigman); + wq.wait(); + } + Ok(0) +} + pub fn sys_kill(pid: i32, sig: i32) -> LinuxResult { if !(1..32).contains(&sig) { return Err(LinuxError::EINVAL); @@ -118,8 +225,18 @@ pub fn sys_kill(pid: i32, sig: i32) -> LinuxResult { let mut result = 0; match pid { 1.. => { - warn!("killing specified pid is not implemented"); - return Err(LinuxError::ENOSYS); + warn!("killing specified pid is only supported for child processes and self"); + if curr.id().as_u64() == pid as u64 { + result += curr.task_ext().signal.lock().send_signal(sig) as isize; + } else { + let children = curr.task_ext().children.lock(); + let child = children.iter().find(|it| it.id().as_u64() == pid as u64); + if let Some(child) = child { + result += child.task_ext().signal.lock().send_signal(sig) as isize; + } else { + return Err(LinuxError::ESRCH); + } + } } 0 => { // TODO: this should send signal to the current process group From c05f3b60e5ca79d4de0f34a9bd9a278f33fe73ce Mon Sep 17 00:00:00 2001 From: mivik Date: Tue, 1 Apr 2025 19:04:52 +0800 Subject: [PATCH 12/16] feat: move ptr to individual crate --- Cargo.lock | 32 ++--- Cargo.toml | 3 +- src/main.rs | 1 - src/mm.rs | 18 +-- src/ptr.rs | 239 ------------------------------- src/signal/mod.rs | 27 ++-- src/syscall_imp/fs/ctl.rs | 38 +++-- src/syscall_imp/fs/io.rs | 24 ++-- src/syscall_imp/fs/mount.rs | 15 +- src/syscall_imp/fs/pipe.rs | 11 +- src/syscall_imp/fs/stat.rs | 37 ++--- src/syscall_imp/mm/brk.rs | 4 - src/syscall_imp/mm/mmap.rs | 35 ++--- src/syscall_imp/mod.rs | 34 +---- src/syscall_imp/signal.rs | 83 +++++------ src/syscall_imp/sys.rs | 8 +- src/syscall_imp/task/schedule.rs | 10 +- src/syscall_imp/task/thread.rs | 42 +++--- src/syscall_imp/utils/time.rs | 35 +++-- src/task.rs | 25 ++-- 20 files changed, 195 insertions(+), 526 deletions(-) delete mode 100644 src/ptr.rs diff --git a/Cargo.lock b/Cargo.lock index da89217c1..8303133ce 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -430,6 +430,19 @@ dependencies = [ "lazyinit", ] +[[package]] +name = "axptr" +version = "0.1.0" +source = "git+https://github.com/Starry-OS/axptr.git#df2459f658316618b787af09b205a5f509352dc7" +dependencies = [ + "axerrno", + "axhal", + "axmm", + "axtask", + "memory_addr", + "percpu", +] + [[package]] name = "axruntime" version = "0.1.0" @@ -1013,22 +1026,6 @@ dependencies = [ "log", ] -[[package]] -name = "macro_rules_attribute" -version = "0.2.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8a82271f7bc033d84bbca59a3ce3e4159938cb08a9c3aebbe54d215131518a13" -dependencies = [ - "macro_rules_attribute-proc_macro", - "paste", -] - -[[package]] -name = "macro_rules_attribute-proc_macro" -version = "0.2.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b8dd856d451cc0da70e2ef2ce95a18e39a93b7558bedf10201ad28503f918568" - [[package]] name = "managed" version = "0.8.0" @@ -1425,6 +1422,7 @@ dependencies = [ "axhal", "axmm", "axns", + "axptr", "axruntime", "axstd", "axsync", @@ -1434,11 +1432,9 @@ dependencies = [ "kernel-elf-parser", "linkme", "log", - "macro_rules_attribute", "memory_addr", "num_enum", "numeric-enum-macro", - "percpu", "spin", "static_assertions", "syscalls", diff --git a/Cargo.toml b/Cargo.toml index bf629d7b3..6f8b86b09 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -18,14 +18,12 @@ xmas-elf = "0.9" spin = "0.9" crate_interface = "0.1" bitflags = "2.6" -percpu = "0.2.0" kernel-elf-parser = "0.3" num_enum = { version = "0.7", default-features = false } syscalls = { version = "0.6", default-features = false } numeric-enum-macro = "0.2.0" static_assertions = "1.1.0" -macro_rules_attribute = "0.2.0" axconfig = { git = "https://github.com/oscomp/arceos.git" } axfs = { git = "https://github.com/oscomp/arceos.git" } @@ -37,6 +35,7 @@ axsync = { git = "https://github.com/oscomp/arceos.git" } axruntime = { git = "https://github.com/oscomp/arceos.git", features = ["multitask"] } arceos_posix_api = { git = "https://github.com/oscomp/arceos.git", features = ["uspace", "smp", "irq", "fs", "multitask", "net", "pipe", "select", "epoll"] } axns = { git = "https://github.com/oscomp/arceos.git", features = ["thread-local"] } +axptr = { git = "https://github.com/Starry-OS/axptr.git" } [patch.crates-io] syscalls = { git = "https://github.com/jasonwhite/syscalls.git", rev = "92624de"} diff --git a/src/main.rs b/src/main.rs index 081473013..8ca77e850 100644 --- a/src/main.rs +++ b/src/main.rs @@ -9,7 +9,6 @@ extern crate alloc; mod ctypes; mod mm; -mod ptr; mod signal; mod syscall_imp; mod task; diff --git a/src/mm.rs b/src/mm.rs index 4c8418970..80b4fa1fc 100644 --- a/src/mm.rs +++ b/src/mm.rs @@ -153,27 +153,13 @@ pub fn load_user_app( Ok((entry, user_sp)) } -#[percpu::def_percpu] -static mut ACCESSING_USER_MEM: bool = false; - -/// Enables scoped access into user memory, allowing page faults to occur inside -/// kernel. -pub fn access_user_memory(f: impl FnOnce() -> R) -> R { - ACCESSING_USER_MEM.with_current(|v| { - *v = true; - let result = f(); - *v = false; - result - }) -} - #[register_trap_handler(PAGE_FAULT)] fn handle_page_fault(vaddr: VirtAddr, access_flags: MappingFlags, is_user: bool) -> bool { warn!( "Page fault at {:#x}, access_flags: {:#x?}", vaddr, access_flags ); - if !is_user && !ACCESSING_USER_MEM.read_current() { + if !is_user && !axptr::is_accessing_user_memory() { return false; } @@ -188,7 +174,7 @@ fn handle_page_fault(vaddr: VirtAddr, access_flags: MappingFlags, is_user: bool) axtask::current().id_name(), vaddr ); - axtask::exit(-1); + crate::task::exit(-1); } true } diff --git a/src/ptr.rs b/src/ptr.rs deleted file mode 100644 index 17fce3f44..000000000 --- a/src/ptr.rs +++ /dev/null @@ -1,239 +0,0 @@ -use axerrno::{LinuxError, LinuxResult}; -use axhal::paging::MappingFlags; -use axtask::{TaskExtRef, current}; -use memory_addr::{MemoryAddr, PAGE_SIZE_4K, VirtAddr, VirtAddrRange}; - -use core::{alloc::Layout, ffi::c_char, mem, slice, str}; - -use crate::mm::access_user_memory; - -fn check_region(start: VirtAddr, layout: Layout, access_flags: MappingFlags) -> LinuxResult<()> { - let align = layout.align(); - if start.as_usize() & (align - 1) != 0 { - return Err(LinuxError::EFAULT); - } - - let task = current(); - let mut aspace = task.task_ext().aspace.lock(); - - if !aspace.check_region_access( - VirtAddrRange::from_start_size(start, layout.size()), - access_flags, - ) { - return Err(LinuxError::EFAULT); - } - - let page_start = start.align_down_4k(); - let page_end = (start + layout.size()).align_up_4k(); - aspace.populate_area(page_start, page_end - page_start)?; - - Ok(()) -} - -fn check_null_terminated( - start: VirtAddr, - access_flags: MappingFlags, -) -> LinuxResult<(*const T, usize)> { - let align = Layout::new::().align(); - if start.as_usize() & (align - 1) != 0 { - return Err(LinuxError::EFAULT); - } - - let zero = T::default(); - - let mut page = start.align_down_4k(); - - let start = start.as_ptr_of::(); - let mut len = 0; - - access_user_memory(|| { - loop { - // SAFETY: This won't overflow the address space since we'll check - // it below. - let ptr = unsafe { start.add(len) }; - while ptr as usize >= page.as_ptr() as usize { - // We cannot prepare `aspace` outside of the loop, since holding - // aspace requires a mutex which would be required on page - // fault, and page faults can trigger inside the loop. - - // TODO: this is inefficient, but we have to do this instead of - // querying the page table since the page might has not been - // allocated yet. - let task = current(); - let aspace = task.task_ext().aspace.lock(); - if !aspace.check_region_access( - VirtAddrRange::from_start_size(page, PAGE_SIZE_4K), - access_flags, - ) { - return Err(LinuxError::EFAULT); - } - - page += PAGE_SIZE_4K; - } - - // This might trigger a page fault - // SAFETY: The pointer is valid and points to a valid memory region. - if unsafe { ptr.read_volatile() } == zero { - break; - } - len += 1; - } - Ok(()) - })?; - - Ok((start, len)) -} - -/// A trait representing a pointer in user space, which can be converted to a -/// pointer in kernel space through a series of checks. -/// -/// Converting a `PtrWrapper` to `*T` is done by `PtrWrapper::get` (or -/// `get_as_*`). It checks whether the pointer along with its layout is valid in -/// the current task's address space, and raises EFAULT if not. -pub trait PtrWrapper: Sized { - type Ptr; - - const ACCESS_FLAGS: MappingFlags; - - /// Unwrap the pointer to the inner type. - /// - /// This function is unsafe because it assumes that the pointer is valid and - /// points to a valid memory region. - unsafe fn into_inner(self) -> Self::Ptr; - - /// Get the address of the pointer. - fn address(&self) -> VirtAddr; - - /// Get the pointer as a raw pointer to `T`. - fn get(self) -> LinuxResult { - self.get_as(Layout::new::()) - } - - /// Get the pointer as a raw pointer to `T`, validating the memory - /// region given by the layout. - fn get_as(self, layout: Layout) -> LinuxResult { - check_region(self.address(), layout, Self::ACCESS_FLAGS)?; - unsafe { Ok(self.into_inner()) } - } - - /// Get the pointer as a raw pointer to `T`, validating the memory - /// region specified by the size. - fn get_as_bytes(self, size: usize) -> LinuxResult { - check_region( - self.address(), - Layout::from_size_align(size, 1).unwrap(), - Self::ACCESS_FLAGS, - )?; - unsafe { Ok(self.into_inner()) } - } - - /// Get the pointer as a raw pointer to `T`, validating the memory - /// region given by the layout of `[T; len]`. - fn get_as_array(self, len: usize) -> LinuxResult { - check_region( - self.address(), - Layout::array::(len).unwrap(), - Self::ACCESS_FLAGS, - )?; - unsafe { Ok(self.into_inner()) } - } - - fn nullable(self, f: impl FnOnce(Self) -> LinuxResult) -> LinuxResult> { - if self.address().as_ptr().is_null() { - Ok(None) - } else { - f(self).map(Some) - } - } -} - -/// A pointer to user space memory. -/// -/// See [`PtrWrapper`] for more details. -#[repr(transparent)] -pub struct UserPtr(*mut T); - -impl From for UserPtr { - fn from(value: usize) -> Self { - UserPtr(value as *mut _) - } -} - -impl PtrWrapper for UserPtr { - type Ptr = *mut T; - - const ACCESS_FLAGS: MappingFlags = MappingFlags::READ.union(MappingFlags::WRITE); - - unsafe fn into_inner(self) -> Self::Ptr { - self.0 - } - - fn address(&self) -> VirtAddr { - VirtAddr::from_mut_ptr_of(self.0) - } -} - -impl UserPtr { - /// Get the pointer as `&mut [T]`, terminated by a null value, validating - /// the memory region. - pub fn get_as_null_terminated(self) -> LinuxResult<&'static mut [T]> - where - T: Eq + Default, - { - let (ptr, len) = check_null_terminated::(self.address(), Self::ACCESS_FLAGS)?; - // SAFETY: We've validated the memory region. - unsafe { Ok(slice::from_raw_parts_mut(ptr as *mut _, len)) } - } -} - -/// An immutable pointer to user space memory. -/// -/// See [`PtrWrapper`] for more details. -#[repr(transparent)] -pub struct UserConstPtr(*const T); - -impl From for UserConstPtr { - fn from(value: usize) -> Self { - UserConstPtr(value as *const _) - } -} - -impl PtrWrapper for UserConstPtr { - type Ptr = *const T; - - const ACCESS_FLAGS: MappingFlags = MappingFlags::READ; - - unsafe fn into_inner(self) -> Self::Ptr { - self.0 - } - - fn address(&self) -> VirtAddr { - VirtAddr::from_ptr_of(self.0) - } -} - -impl UserConstPtr { - /// Get the pointer as `&[T]`, terminated by a null value, validating the - /// memory region. - pub fn get_as_null_terminated(self) -> LinuxResult<&'static [T]> - where - T: Eq + Default, - { - let (ptr, len) = check_null_terminated::(self.address(), Self::ACCESS_FLAGS)?; - // SAFETY: We've validated the memory region. - unsafe { Ok(slice::from_raw_parts(ptr, len)) } - } -} - -static_assertions::const_assert_eq!(size_of::(), size_of::()); - -impl UserConstPtr { - /// Get the pointer as `&str`, validating the memory region. - pub fn get_as_str(self) -> LinuxResult<&'static str> { - let slice = self.get_as_null_terminated()?; - // SAFETY: c_char is u8 - let slice = unsafe { mem::transmute::<&[c_char], &[u8]>(slice) }; - - str::from_utf8(slice).map_err(|_| LinuxError::EILSEQ) - } -} diff --git a/src/signal/mod.rs b/src/signal/mod.rs index ebf7d661d..9d7af0d5a 100644 --- a/src/signal/mod.rs +++ b/src/signal/mod.rs @@ -4,12 +4,11 @@ use core::alloc::Layout; use alloc::sync::Arc; use axhal::{arch::TrapFrame, trap::POST_TRAP}; -use axtask::{TaskExtRef, WaitQueue, current, exit}; +use axptr::{UserConstPtr, UserPtr}; +use axtask::{TaskExtRef, WaitQueue, current}; use ctypes::{SignalAction, SignalActionFlags, SignalDisposition, SignalInfo, SignalSet}; use linkme::distributed_slice as register_trap_handler; -use crate::ptr::{PtrWrapper, UserConstPtr, UserPtr}; - pub const SIGKILL: u32 = 9; pub const SIGSTOP: u32 = 19; @@ -181,14 +180,14 @@ impl SignalManager { let action = &self.actions[signo as usize]; match action.disposition { SignalDisposition::Default => match DEFAULT_ACTIONS[signo as usize] { - DefaultSignalAction::Terminate => exit(128 + signo as i32), + DefaultSignalAction::Terminate => crate::task::exit(128 + signo as i32), DefaultSignalAction::CoreDump => { warn!("Core dump not implemented"); - exit(128 + signo as i32); + crate::task::exit(128 + signo as i32); } DefaultSignalAction::Stop => { warn!("Stop not implemented"); - exit(-1); + crate::task::exit(-1); } DefaultSignalAction::Ignore => false, DefaultSignalAction::Continue => { @@ -201,10 +200,10 @@ impl SignalManager { let layout = Layout::new::(); let aligned_sp = (tf.sp() - layout.size()) & !(layout.align() - 1); - let frame_ptr: UserPtr = aligned_sp.into(); - let frame_ptr = frame_ptr.get().expect("invalid frame ptr"); - // SAFETY: pointer is valid - let frame = unsafe { &mut *frame_ptr }; + let mut frame_ptr: UserPtr = aligned_sp.into(); + let frame = frame_ptr + .get(current().task_ext()) + .expect("invalid frame ptr"); *frame = SignalFrame { tf: *tf, @@ -216,7 +215,7 @@ impl SignalManager { tf.set_sp(aligned_sp); tf.set_arg0(signo as _); tf.set_arg1(&frame.siginfo as *const _ as _); - tf.set_arg2(frame_ptr as _); + tf.set_arg2(&frame as *const _ as _); let restorer = action.restorer.map_or(0, |f| f as _); #[cfg(target_arch = "x86_64")] @@ -262,9 +261,9 @@ fn post_trap_callback(tf: &mut TrapFrame, from_user: bool) { pub fn sigreturn(tf: &mut TrapFrame) { let frame_ptr: UserConstPtr = tf.sp().into(); - let frame = frame_ptr.get().expect("invalid frame ptr"); - // SAFETY: pointer is valid - let frame = unsafe { &*frame }; + let frame = frame_ptr + .get(current().task_ext()) + .expect("invalid frame ptr"); *tf = frame.tf; #[cfg(any( diff --git a/src/syscall_imp/fs/ctl.rs b/src/syscall_imp/fs/ctl.rs index 54b9c4a90..232b34a51 100644 --- a/src/syscall_imp/fs/ctl.rs +++ b/src/syscall_imp/fs/ctl.rs @@ -3,12 +3,8 @@ use core::ffi::{c_char, c_void}; use alloc::string::ToString; use arceos_posix_api::AT_FDCWD; use axerrno::{AxError, LinuxError, LinuxResult}; -use macro_rules_attribute::apply; - -use crate::{ - ptr::{PtrWrapper, UserConstPtr, UserPtr}, - syscall_imp::syscall_instrument, -}; +use axptr::{UserConstPtr, UserPtr}; +use axtask::{TaskExtRef, current}; /// The ioctl() system call manipulates the underlying device parameters /// of special files. @@ -18,14 +14,13 @@ use crate::{ /// * `op` - The request code. It is of type unsigned long in glibc and BSD, /// and of type int in musl and other UNIX systems. /// * `argp` - The argument to the request. It is a pointer to a memory location -#[apply(syscall_instrument)] pub fn sys_ioctl(_fd: i32, _op: usize, _argp: UserPtr) -> LinuxResult { warn!("Unimplemented syscall: SYS_IOCTL"); Ok(0) } pub fn sys_chdir(path: UserConstPtr) -> LinuxResult { - let path = path.get_as_str()?; + let path = path.get_as_str(current().task_ext())?; axfs::api::set_current_dir(path).map(|_| 0).map_err(|err| { warn!("Failed to change directory: {err:?}"); err.into() @@ -33,7 +28,7 @@ pub fn sys_chdir(path: UserConstPtr) -> LinuxResult { } pub(crate) fn sys_mkdirat(dirfd: i32, path: UserConstPtr, mode: u32) -> LinuxResult { - let path = path.get_as_str()?; + let path = path.get_as_str(current().task_ext())?; if !path.starts_with("/") && dirfd != AT_FDCWD as i32 { warn!("unsupported."); @@ -142,8 +137,9 @@ impl<'a> DirBuffer<'a> { } } -pub fn sys_getdents64(fd: i32, buf: UserPtr, len: usize) -> LinuxResult { - let buf = buf.get_as_bytes(len)?; +pub fn sys_getdents64(fd: i32, mut buf: UserPtr, len: usize) -> LinuxResult { + let buf_address = buf.address().as_usize(); + let buf = buf.get_as_slice(current().task_ext(), len)?; if len < DirEnt::FIXED_SIZE { warn!("Buffer size too small: {len}"); @@ -158,14 +154,15 @@ pub fn sys_getdents64(fd: i32, buf: UserPtr, len: usize) -> LinuxResult< } }; - let mut buffer = - unsafe { DirBuffer::new(core::slice::from_raw_parts_mut(buf as *mut u8, len)) }; + let mut buffer = DirBuffer::new(buf); - let (initial_offset, count) = unsafe { + let (initial_offset, count) = { let mut buf_offset = 0; let mut count = 0; while buf_offset + DirEnt::FIXED_SIZE <= len { - let dir_ent = *(buf.add(buf_offset) as *const DirEnt); + let dir_ent: UserConstPtr = (buf_address + buf_offset).into(); + let dir_ent = dir_ent.get(current().task_ext())?; + if dir_ent.d_reclen == 0 { break; } @@ -226,8 +223,8 @@ pub fn sys_linkat( new_path: UserConstPtr, flags: i32, ) -> LinuxResult { - let old_path = old_path.get_as_null_terminated()?; - let new_path = new_path.get_as_null_terminated()?; + let old_path = old_path.get_as_null_terminated(current().task_ext())?; + let new_path = new_path.get_as_null_terminated(current().task_ext())?; if flags != 0 { warn!("Unsupported flags: {flags}"); @@ -262,7 +259,7 @@ pub fn sys_linkat( /// flags: can be 0 or AT_REMOVEDIR /// return 0 when success, else return -1 pub fn sys_unlinkat(dir_fd: isize, path: UserConstPtr, flags: usize) -> LinuxResult { - let path = path.get_as_null_terminated()?; + let path = path.get_as_null_terminated(current().task_ext())?; const AT_REMOVEDIR: usize = 0x200; @@ -293,6 +290,7 @@ pub fn sys_unlinkat(dir_fd: isize, path: UserConstPtr, flags: usize) -> .map_err(|err| err.into()) } -pub fn sys_getcwd(buf: UserPtr, size: usize) -> LinuxResult { - Ok(arceos_posix_api::sys_getcwd(buf.get_as_null_terminated()?.as_ptr() as _, size) as _) +pub fn sys_getcwd(mut buf: UserPtr, size: usize) -> LinuxResult { + let buf = buf.get_as_slice(current().task_ext(), size + 1)?; + Ok(arceos_posix_api::sys_getcwd(buf.as_mut_ptr() as _, size) as _) } diff --git a/src/syscall_imp/fs/io.rs b/src/syscall_imp/fs/io.rs index 37c76c504..0d65d22be 100644 --- a/src/syscall_imp/fs/io.rs +++ b/src/syscall_imp/fs/io.rs @@ -1,18 +1,18 @@ -use core::ffi::{c_char, c_void}; +use core::ffi::c_char; use arceos_posix_api::{self as api, ctypes::mode_t}; use axerrno::LinuxResult; +use axptr::{UserConstPtr, UserPtr}; +use axtask::{TaskExtRef, current}; -use crate::ptr::{PtrWrapper, UserConstPtr, UserPtr}; - -pub(crate) fn sys_read(fd: i32, buf: UserPtr, count: usize) -> LinuxResult { - let buf = buf.get_as_bytes(count)?; - Ok(api::sys_read(fd, buf, count)) +pub(crate) fn sys_read(fd: i32, mut buf: UserPtr, count: usize) -> LinuxResult { + let buf = buf.get_as_slice(current().task_ext(), count)?; + Ok(api::sys_read(fd, buf.as_mut_ptr().cast(), count)) } -pub(crate) fn sys_write(fd: i32, buf: UserConstPtr, count: usize) -> LinuxResult { - let buf = buf.get_as_bytes(count)?; - Ok(api::sys_write(fd, buf, count)) +pub(crate) fn sys_write(fd: i32, buf: UserConstPtr, count: usize) -> LinuxResult { + let buf = buf.get_as_slice(current().task_ext(), count)?; + Ok(api::sys_write(fd, buf.as_ptr().cast(), count)) } pub(crate) fn sys_writev( @@ -20,8 +20,8 @@ pub(crate) fn sys_writev( iov: UserConstPtr, iocnt: i32, ) -> LinuxResult { - let iov = iov.get_as_bytes(iocnt as _)?; - unsafe { Ok(api::sys_writev(fd, iov, iocnt)) } + let iov = iov.get_as_slice(current().task_ext(), iocnt as _)?; + unsafe { Ok(api::sys_writev(fd, iov.as_ptr().cast(), iocnt)) } } pub(crate) fn sys_openat( @@ -30,7 +30,7 @@ pub(crate) fn sys_openat( flags: i32, modes: mode_t, ) -> LinuxResult { - let path = path.get_as_null_terminated()?; + let path = path.get_as_str(current().task_ext())?; Ok(api::sys_openat(dirfd, path.as_ptr(), flags, modes) as _) } diff --git a/src/syscall_imp/fs/mount.rs b/src/syscall_imp/fs/mount.rs index 0bbdbd899..0ac31c148 100644 --- a/src/syscall_imp/fs/mount.rs +++ b/src/syscall_imp/fs/mount.rs @@ -1,11 +1,11 @@ use alloc::vec::Vec; use arceos_posix_api::{AT_FDCWD, FilePath, handle_file_path}; use axerrno::{LinuxError, LinuxResult}; +use axptr::UserConstPtr; use axsync::Mutex; +use axtask::{TaskExtRef, current}; use core::ffi::{c_char, c_void}; -use crate::ptr::UserConstPtr; - pub fn sys_mount( source: UserConstPtr, target: UserConstPtr, @@ -14,9 +14,10 @@ pub fn sys_mount( _data: UserConstPtr, ) -> LinuxResult { info!("sys_mount"); - let source = source.get_as_null_terminated()?; - let target = target.get_as_null_terminated()?; - let fs_type = fs_type.get_as_str()?; + let curr = current(); + let source = source.get_as_null_terminated(curr.task_ext())?; + let target = target.get_as_null_terminated(curr.task_ext())?; + let fs_type = fs_type.get_as_str(curr.task_ext())?; let device_path = handle_file_path(AT_FDCWD, Some(source.as_ptr() as _), false)?; let mount_path = handle_file_path(AT_FDCWD, Some(target.as_ptr() as _), true)?; info!( @@ -48,8 +49,8 @@ pub fn sys_mount( pub fn sys_umount2(target: UserConstPtr, flags: i32) -> LinuxResult { info!("sys_umount2"); - let target = target.get_as_null_terminated()?; - let mount_path = handle_file_path(AT_FDCWD, Some(target.as_ptr() as _), true)?; + let target = target.get_as_str(current().task_ext())?; + let mount_path = handle_file_path(AT_FDCWD, Some(target.as_ptr()), true)?; if flags != 0 { debug!("flags unimplemented"); return Err(LinuxError::EPERM); diff --git a/src/syscall_imp/fs/pipe.rs b/src/syscall_imp/fs/pipe.rs index 323f2c1ec..ffc6c2e5f 100644 --- a/src/syscall_imp/fs/pipe.rs +++ b/src/syscall_imp/fs/pipe.rs @@ -2,11 +2,10 @@ use core::ffi::c_int; use arceos_posix_api as api; use axerrno::LinuxResult; +use axptr::UserPtr; +use axtask::{current, TaskExtRef}; -use crate::ptr::{PtrWrapper, UserPtr}; - -pub fn sys_pipe2(fds: UserPtr) -> LinuxResult { - let fds = fds.get_as_array(2)?; - let fds_slice: &mut [c_int] = unsafe { core::slice::from_raw_parts_mut(fds, 2) }; - Ok(api::sys_pipe(fds_slice) as _) +pub fn sys_pipe2(mut fds: UserPtr) -> LinuxResult { + let fds = fds.get_as_slice(current().task_ext(), 2)?; + Ok(api::sys_pipe(fds) as _) } diff --git a/src/syscall_imp/fs/stat.rs b/src/syscall_imp/fs/stat.rs index 626653468..0019b4838 100644 --- a/src/syscall_imp/fs/stat.rs +++ b/src/syscall_imp/fs/stat.rs @@ -1,12 +1,8 @@ use core::ffi::c_char; use axerrno::{LinuxError, LinuxResult}; -use macro_rules_attribute::apply; - -use crate::{ - ptr::{PtrWrapper, UserConstPtr, UserPtr}, - syscall_imp::syscall_instrument, -}; +use axptr::{UserConstPtr, UserPtr}; +use axtask::{TaskExtRef, current}; #[derive(Debug, Clone, Copy, Default)] #[repr(C)] @@ -74,8 +70,8 @@ impl From for Kstat { } } -pub fn sys_fstat(fd: i32, kstatbuf: UserPtr) -> LinuxResult { - let kstatbuf = kstatbuf.get()?; +pub fn sys_fstat(fd: i32, mut kstatbuf: UserPtr) -> LinuxResult { + let kstatbuf = kstatbuf.get(current().task_ext())?; let mut statbuf = arceos_posix_api::ctypes::stat::default(); let result = unsafe { @@ -85,24 +81,21 @@ pub fn sys_fstat(fd: i32, kstatbuf: UserPtr) -> LinuxResult { return Ok(result as _); } - unsafe { - let kstat = Kstat::from(statbuf); - kstatbuf.write(kstat); - } + *kstatbuf = statbuf.into(); + Ok(0) } -#[apply(syscall_instrument)] pub fn sys_fstatat( dir_fd: isize, path: UserConstPtr, - kstatbuf: UserPtr, + mut kstatbuf: UserPtr, _flags: i32, ) -> LinuxResult { - let path = path.get_as_null_terminated()?; + let path = path.get_as_str(current().task_ext())?; let path = arceos_posix_api::handle_file_path(dir_fd, Some(path.as_ptr() as _), false)?; - let kstatbuf = kstatbuf.get()?; + let kstatbuf = kstatbuf.get(current().task_ext())?; let mut statbuf = arceos_posix_api::ctypes::stat::default(); let result = unsafe { @@ -115,10 +108,7 @@ pub fn sys_fstatat( return Ok(result as _); } - unsafe { - let kstat = Kstat::from(statbuf); - kstatbuf.write(kstat); - } + *kstatbuf = statbuf.into(); Ok(0) } @@ -182,13 +172,12 @@ pub struct StatX { pub stx_dio_offset_align: u32, } -#[apply(syscall_instrument)] pub fn sys_statx( dirfd: i32, pathname: UserConstPtr, flags: u32, _mask: u32, - statxbuf: UserPtr, + mut statxbuf: UserPtr, ) -> LinuxResult { // `statx()` uses pathname, dirfd, and flags to identify the target // file in one of the following ways: @@ -217,7 +206,7 @@ pub fn sys_statx( // below), then the target file is the one referred to by the // file descriptor dirfd. - let path = pathname.get_as_str()?; + let path = pathname.get_as_str(current().task_ext())?; const AT_EMPTY_PATH: u32 = 0x1000; if path.is_empty() { @@ -230,7 +219,7 @@ pub fn sys_statx( if res < 0 { return Err(LinuxError::try_from(-res).unwrap()); } - let statx = unsafe { &mut *statxbuf.get()? }; + let statx = statxbuf.get(current().task_ext())?; statx.stx_blksize = status.st_blksize as u32; statx.stx_attributes = status.st_mode as u64; statx.stx_nlink = status.st_nlink; diff --git a/src/syscall_imp/mm/brk.rs b/src/syscall_imp/mm/brk.rs index ad0f97e06..ae867a1cf 100644 --- a/src/syscall_imp/mm/brk.rs +++ b/src/syscall_imp/mm/brk.rs @@ -1,10 +1,6 @@ use axerrno::LinuxResult; use axtask::{TaskExtRef, current}; -use macro_rules_attribute::apply; -use crate::syscall_imp::syscall_instrument; - -#[apply(syscall_instrument)] pub fn sys_brk(addr: usize) -> LinuxResult { let current_task = current(); let mut return_val: isize = current_task.task_ext().get_heap_top() as isize; diff --git a/src/syscall_imp/mm/mmap.rs b/src/syscall_imp/mm/mmap.rs index a9af9069b..a9a6b6820 100644 --- a/src/syscall_imp/mm/mmap.rs +++ b/src/syscall_imp/mm/mmap.rs @@ -1,15 +1,10 @@ use alloc::vec; use axerrno::{LinuxError, LinuxResult}; use axhal::paging::MappingFlags; +use axptr::UserPtr; use axtask::{TaskExtRef, current}; -use macro_rules_attribute::apply; use memory_addr::{VirtAddr, VirtAddrRange}; -use crate::{ - ptr::{PtrWrapper, UserPtr}, - syscall_imp::syscall_instrument, -}; - bitflags::bitflags! { /// permissions for sys_mmap /// @@ -66,7 +61,6 @@ bitflags::bitflags! { } } -#[apply(syscall_instrument)] pub fn sys_mmap( addr: UserPtr, length: usize, @@ -75,8 +69,7 @@ pub fn sys_mmap( fd: i32, offset: isize, ) -> LinuxResult { - // Safety: addr is used for mapping, and we won't directly access it. - let mut addr = unsafe { addr.into_inner() }; + let mut addr = addr.address().as_usize(); let curr = current(); let curr_ext = curr.task_ext(); @@ -87,12 +80,12 @@ pub fn sys_mmap( let map_flags = MmapFlags::from_bits_truncate(flags); let mut aligned_length = length; - if addr.is_null() { + if addr == 0 { aligned_length = memory_addr::align_up_4k(aligned_length); } else { - let start = addr as usize; + let start = addr; let mut end = start + aligned_length; - addr = memory_addr::align_down_4k(start) as *mut usize; + addr = memory_addr::align_down_4k(start).into(); end = memory_addr::align_up_4k(end); aligned_length = end - start; } @@ -103,10 +96,10 @@ pub fn sys_mmap( ); let start_addr = if map_flags.contains(MmapFlags::MAP_FIXED) { - if addr.is_null() { + if addr == 0 { return Err(LinuxError::EINVAL); } - let dst_addr = VirtAddr::from(addr as usize); + let dst_addr = VirtAddr::from(addr); aspace.unmap(dst_addr, aligned_length)?; dst_addr } else { @@ -157,26 +150,17 @@ pub fn sys_mmap( Ok(start_addr.as_usize() as _) } -#[apply(syscall_instrument)] pub fn sys_munmap(addr: UserPtr, length: usize) -> LinuxResult { - // Safety: addr is used for mapping, and we won't directly access it. - let addr = unsafe { addr.into_inner() }; - let curr = current(); let curr_ext = curr.task_ext(); let mut aspace = curr_ext.aspace.lock(); let length = memory_addr::align_up_4k(length); - let start_addr = VirtAddr::from(addr as usize); - aspace.unmap(start_addr, length)?; + aspace.unmap(addr.address(), length)?; axhal::arch::flush_tlb(None); Ok(0) } -#[apply(syscall_instrument)] pub fn sys_mprotect(addr: UserPtr, length: usize, prot: i32) -> LinuxResult { - // Safety: addr is used for mapping, and we won't directly access it. - let addr = unsafe { addr.into_inner() }; - // TODO: implement PROT_GROWSUP & PROT_GROWSDOWN let Some(permission_flags) = MmapProt::from_bits(prot) else { return Err(LinuxError::EINVAL); @@ -189,8 +173,7 @@ pub fn sys_mprotect(addr: UserPtr, length: usize, prot: i32) -> LinuxResu let curr_ext = curr.task_ext(); let mut aspace = curr_ext.aspace.lock(); let length = memory_addr::align_up_4k(length); - let start_addr = VirtAddr::from(addr as usize); - aspace.protect(start_addr, length, permission_flags.into())?; + aspace.protect(addr.address(), length, permission_flags.into())?; Ok(0) } diff --git a/src/syscall_imp/mod.rs b/src/syscall_imp/mod.rs index d4e8e1046..08225ba79 100644 --- a/src/syscall_imp/mod.rs +++ b/src/syscall_imp/mod.rs @@ -20,38 +20,6 @@ use self::sys::*; use self::task::*; use self::utils::*; -macro_rules! syscall_instrument {( - $( #[$attr:meta] )* - $pub:vis - fn $fname:ident ( - $( $arg_name:ident : $ArgTy:ty ),* $(,)? - ) -> $RetTy:ty - $body:block -) => ( - $( #[$attr] )* - #[allow(unused_parens)] - $pub - fn $fname ( - $( $arg_name : $ArgTy ),* - ) -> $RetTy - { - /// Re-emit the original function definition, but as a scoped helper - $( #[$attr] )* - fn __original_func__ ( - $($arg_name: $ArgTy),* - ) -> $RetTy - $body - - let res = __original_func__($($arg_name),*); - match res { - Ok(_) | Err(axerrno::LinuxError::EAGAIN) => debug!(concat!(stringify!($fname), " => {:?}"), res), - Err(_) => info!(concat!(stringify!($fname), " => {:?}"), res), - } - res - } -)} -pub(crate) use syscall_instrument; - #[register_trap_handler(SYSCALL)] fn handle_syscall(tf: &mut TrapFrame, syscall_num: usize) -> isize { time_stat_from_user_to_kernel(); @@ -175,7 +143,7 @@ fn handle_syscall(tf: &mut TrapFrame, syscall_num: usize) -> isize { Sysno::rt_sigsuspend => sys_rt_sigsuspend(tf, tf.arg0().into(), tf.arg1() as _), _ => { warn!("Unimplemented syscall: {}", syscall_num); - axtask::exit(LinuxError::ENOSYS as _) + crate::task::exit(LinuxError::ENOSYS as _) } }; let ans = result.unwrap_or_else(|err| -err.code() as _); diff --git a/src/syscall_imp/signal.rs b/src/syscall_imp/signal.rs index 43152eeb9..797d6540b 100644 --- a/src/syscall_imp/signal.rs +++ b/src/syscall_imp/signal.rs @@ -3,15 +3,13 @@ use core::time::Duration; use arceos_posix_api as api; use axerrno::{LinuxError, LinuxResult}; use axhal::arch::TrapFrame; +use axptr::{UserConstPtr, UserPtr}; use axtask::{TaskExtRef, current}; -use crate::{ - ptr::{PtrWrapper, UserConstPtr, UserPtr}, - signal::{ - SIGKILL, SIGSTOP, - ctypes::{SignalAction, SignalInfo, SignalSet, k_sigaction}, - sigreturn, - }, +use crate::signal::{ + SIGKILL, SIGSTOP, + ctypes::{SignalInfo, SignalSet, k_sigaction}, + sigreturn, }; fn check_sigset_size(size: usize) -> LinuxResult<()> { @@ -32,23 +30,19 @@ pub fn sys_rt_sigprocmask( let curr = current(); let mut sigman = curr.task_ext().signal.lock(); - if let Some(oldset) = oldset.nullable(UserPtr::get)? { - // SAFETY: oldset is a valid pointer - unsafe { - oldset.write(sigman.blocked); - } + if let Some(mut oldset) = oldset.nullable() { + *oldset.get(curr.task_ext())? = sigman.blocked; } - if let Some(set) = set.nullable(UserConstPtr::get)? { - // SAFETY: set is a valid pointer - let set: SignalSet = unsafe { set.read() }; + if let Some(set) = set.nullable() { + let set = set.get(curr.task_ext())?; match how { // SIG_BLOCK 0 => sigman.blocked.add_from(&set), // SIG_UNBLOCK 1 => sigman.blocked.remove_from(&set), // SIG_SETMASK - 2 => sigman.blocked = set, + 2 => sigman.blocked = *set, _ => return Err(LinuxError::EINVAL), } } @@ -75,29 +69,26 @@ pub fn sys_rt_sigaction( let curr = current(); let mut sigman = curr.task_ext().signal.lock(); - if let Some(oldact) = oldact.nullable(UserPtr::get)? { - // SAFETY: oldact is a valid pointer - sigman.action(signum).to_ctype(unsafe { &mut *oldact }); + if let Some(mut oldact) = oldact.nullable() { + let oldact = oldact.get(curr.task_ext())?; + sigman.action(signum).to_ctype(oldact); } - if let Some(act) = act.nullable(UserConstPtr::get)? { - // SAFETY: act is a valid pointer - let action: SignalAction = unsafe { act.read() }.try_into()?; - sigman.set_action(signum, action); + if let Some(act) = act.nullable() { + let act = act.get(curr.task_ext())?; + sigman.set_action(signum, (*act).try_into()?); } Ok(0) } -pub fn sys_rt_sigpending(set: UserPtr, sigsetsize: usize) -> LinuxResult { +pub fn sys_rt_sigpending(mut set: UserPtr, sigsetsize: usize) -> LinuxResult { check_sigset_size(sigsetsize)?; let curr = current(); let sigman = curr.task_ext().signal.lock(); - let set = set.get()?; - // SAFETY: set is a valid pointer - unsafe { set.write(sigman.pending) }; + *set.get(curr.task_ext())? = sigman.pending; Ok(0) } @@ -115,26 +106,25 @@ pub fn sys_rt_sigtimedwait( ) -> LinuxResult { check_sigset_size(sigsetsize)?; - let set = set.get()?; - // SAFETY: set is a valid pointer - let mut set = SignalSet::from(unsafe { set.read() }); + let curr = current(); + let mut set = *set.get(curr.task_ext())?; - let timeout = timeout.nullable(UserConstPtr::get)?.map(|spec| { - // SAFETY: spec is a valid pointer - let spec = unsafe { spec.read() }; - Duration::new(spec.tv_sec as u64, spec.tv_nsec as u32) - }); + let timeout = timeout + .nullable() + .map(|spec| { + spec.get(curr.task_ext()) + .map(|spec| Duration::new(spec.tv_sec as u64, spec.tv_nsec as u32)) + }) + .transpose()?; - let curr = current(); let mut sigman = curr.task_ext().signal.lock(); // Non-blocked signals cannot be waited set.remove_from(&!sigman.blocked); if let Some(siginfo) = sigman.dequeue_signal_in(&set) { - if let Some(info) = info.nullable(UserPtr::get)? { - // SAFETY: info is a valid pointer - unsafe { info.write(siginfo) }; + if let Some(mut info) = info.nullable() { + *info.get(curr.task_ext())? = siginfo; } return Ok(0); } @@ -151,9 +141,8 @@ pub fn sys_rt_sigtimedwait( let mut sigman = curr.task_ext().signal.lock(); if let Some(siginfo) = sigman.dequeue_signal_in(&set) { - if let Some(info) = info.nullable(UserPtr::get)? { - // SAFETY: info is a valid pointer - unsafe { info.write(siginfo) }; + if let Some(mut info) = info.nullable() { + *info.get(curr.task_ext())? = siginfo; } return Ok(0); } @@ -165,15 +154,13 @@ pub fn sys_rt_sigtimedwait( pub fn sys_rt_sigsuspend( tf: &mut TrapFrame, - set: UserPtr, + mut set: UserPtr, sigsetsize: usize, ) -> LinuxResult { check_sigset_size(sigsetsize)?; let curr = current(); - let set = set.get()?; - // SAFETY: set is a valid pointer - let mut set = unsafe { *set }; + let set = set.get(curr.task_ext())?; set.remove(SIGKILL); set.remove(SIGSTOP); @@ -181,8 +168,8 @@ pub fn sys_rt_sigsuspend( let mut sigman = curr.task_ext().signal.lock(); let old_blocked = sigman.blocked; - sigman.blocked = set; - sigman.waiting = !set; + sigman.blocked = *set; + sigman.waiting = !*set; drop(sigman); // TODO: document this diff --git a/src/syscall_imp/sys.rs b/src/syscall_imp/sys.rs index ccfc15661..cc5df28ff 100644 --- a/src/syscall_imp/sys.rs +++ b/src/syscall_imp/sys.rs @@ -1,6 +1,6 @@ use axerrno::LinuxResult; - -use crate::ptr::{PtrWrapper, UserPtr}; +use axptr::UserPtr; +use axtask::{current, TaskExtRef}; pub fn sys_getuid() -> LinuxResult { Ok(0) @@ -43,7 +43,7 @@ impl UtsName { } } -pub fn sys_uname(name: UserPtr) -> LinuxResult { - unsafe { *name.get()? = UtsName::default() }; +pub fn sys_uname(mut name: UserPtr) -> LinuxResult { + *name.get(current().task_ext())? = UtsName::default(); Ok(0) } diff --git a/src/syscall_imp/task/schedule.rs b/src/syscall_imp/task/schedule.rs index 692311ed5..e4671325c 100644 --- a/src/syscall_imp/task/schedule.rs +++ b/src/syscall_imp/task/schedule.rs @@ -1,7 +1,7 @@ use arceos_posix_api as api; use axerrno::LinuxResult; - -use crate::ptr::{PtrWrapper, UserConstPtr, UserPtr}; +use axptr::{UserConstPtr, UserPtr}; +use axtask::{TaskExtRef, current}; pub fn sys_sched_yield() -> LinuxResult { Ok(api::sys_sched_yield() as _) @@ -9,7 +9,9 @@ pub fn sys_sched_yield() -> LinuxResult { pub fn sys_nanosleep( req: UserConstPtr, - rem: UserPtr, + mut rem: UserPtr, ) -> LinuxResult { - unsafe { Ok(api::sys_nanosleep(req.get()?, rem.get()?) as _) } + let req = req.get(current().task_ext())?; + let rem = rem.get(current().task_ext())?; + unsafe { Ok(api::sys_nanosleep(req, rem) as _) } } diff --git a/src/syscall_imp/task/thread.rs b/src/syscall_imp/task/thread.rs index d72fd187a..7e0659048 100644 --- a/src/syscall_imp/task/thread.rs +++ b/src/syscall_imp/task/thread.rs @@ -1,15 +1,13 @@ -use core::{ffi::c_char, ptr}; +use core::{ffi::c_char, ops::DerefMut}; use alloc::vec::Vec; use axerrno::{LinuxError, LinuxResult}; +use axptr::{UserConstPtr, UserPtr}; use axtask::{TaskExtRef, current, yield_now}; -use macro_rules_attribute::apply; use num_enum::TryFromPrimitive; use crate::{ ctypes::{WaitFlags, WaitStatus}, - ptr::{PtrWrapper, UserConstPtr, UserPtr}, - syscall_imp::syscall_instrument, task::wait_pid, }; @@ -34,12 +32,10 @@ enum ArchPrctlCode { SetCpuid = 0x1012, } -#[apply(syscall_instrument)] pub fn sys_getpid() -> LinuxResult { Ok(axtask::current().task_ext().proc_id as _) } -#[apply(syscall_instrument)] pub fn sys_getppid() -> LinuxResult { Ok(axtask::current().task_ext().get_parent() as _) } @@ -55,22 +51,21 @@ pub fn sys_exit(status: i32) -> ! { } // TODO: wake up threads, which are blocked by futex, and waiting for the address pointed by clear_child_tid } - axtask::exit(status); + crate::task::exit(status); } pub fn sys_exit_group(status: i32) -> ! { warn!("Temporarily replace sys_exit_group with sys_exit"); - axtask::exit(status); + crate::task::exit(status); } /// To set the clear_child_tid field in the task extended data. /// /// The set_tid_address() always succeeds -#[apply(syscall_instrument)] pub fn sys_set_tid_address(tid_ptd: UserConstPtr) -> LinuxResult { let curr = current(); curr.task_ext() - .set_clear_child_tid(tid_ptd.address().as_ptr() as _); + .set_clear_child_tid(tid_ptd.address().as_usize() as _); Ok(curr.id().as_u64() as isize) } @@ -111,7 +106,6 @@ pub fn sys_arch_prctl(code: i32, addr: UserPtr) -> LinuxResult { } } -#[apply(syscall_instrument)] pub fn sys_clone( flags: usize, user_stack: usize, @@ -144,12 +138,17 @@ pub fn sys_clone( } } -#[apply(syscall_instrument)] -pub fn sys_wait4(pid: i32, exit_code_ptr: UserPtr, option: u32) -> LinuxResult { +pub fn sys_wait4(pid: i32, exit_code: UserPtr, option: u32) -> LinuxResult { let option_flag = WaitFlags::from_bits(option).unwrap(); - let exit_code_ptr = exit_code_ptr.nullable(UserPtr::get)?; + let mut exit_code = exit_code.nullable(); loop { - let answer = wait_pid(pid, exit_code_ptr.unwrap_or_else(ptr::null_mut)); + let answer = wait_pid( + pid, + exit_code + .as_mut() + .map(|p| p.get(current().task_ext())) + .transpose()?, + ); match answer { Ok(pid) => { return Ok(pid as isize); @@ -173,29 +172,30 @@ pub fn sys_wait4(pid: i32, exit_code_ptr: UserPtr, option: u32) -> LinuxRes } } -#[apply(syscall_instrument)] pub fn sys_execve( path: UserConstPtr, argv: UserConstPtr, envp: UserConstPtr, ) -> LinuxResult { - let path_str = path.get_as_str()?; + let curr = current(); + let mut aspace = curr.task_ext().aspace.lock(); + let path_str = path.get_as_str(aspace.deref_mut())?; let args = argv - .get_as_null_terminated()? + .get_as_null_terminated(aspace.deref_mut())? .iter() .map(|arg| { UserConstPtr::::from(*arg) - .get_as_str() + .get_as_str(aspace.deref_mut()) .map(Into::into) }) .collect::, _>>()?; let envs = envp - .get_as_null_terminated()? + .get_as_null_terminated(aspace.deref_mut())? .iter() .map(|env| { UserConstPtr::::from(*env) - .get_as_str() + .get_as_str(aspace.deref_mut()) .map(Into::into) }) .collect::, _>>()?; diff --git a/src/syscall_imp/utils/time.rs b/src/syscall_imp/utils/time.rs index 323b468af..882316924 100644 --- a/src/syscall_imp/utils/time.rs +++ b/src/syscall_imp/utils/time.rs @@ -1,30 +1,29 @@ use arceos_posix_api::{self as api, ctypes::timeval}; use axerrno::LinuxResult; use axhal::time::{monotonic_time_nanos, nanos_to_ticks}; +use axptr::UserPtr; +use axtask::{TaskExtRef, current}; -use crate::{ - ctypes::Tms, - ptr::{PtrWrapper, UserPtr}, - task::time_stat_output, -}; +use crate::{ctypes::Tms, task::time_stat_output}; -pub fn sys_clock_gettime(clock_id: i32, tp: UserPtr) -> LinuxResult { - unsafe { Ok(api::sys_clock_gettime(clock_id, tp.get()?) as _) } +pub fn sys_clock_gettime( + clock_id: i32, + mut tp: UserPtr, +) -> LinuxResult { + unsafe { Ok(api::sys_clock_gettime(clock_id, tp.get(current().task_ext())?) as _) } } -pub fn sys_get_time_of_day(ts: UserPtr) -> LinuxResult { - unsafe { Ok(api::sys_get_time_of_day(ts.get()?) as _) } +pub fn sys_get_time_of_day(mut ts: UserPtr) -> LinuxResult { + unsafe { Ok(api::sys_get_time_of_day(ts.get(current().task_ext())?) as _) } } -pub fn sys_times(tms: UserPtr) -> LinuxResult { +pub fn sys_times(mut tms: UserPtr) -> LinuxResult { let (_, utime_us, _, stime_us) = time_stat_output(); - unsafe { - *tms.get()? = Tms { - tms_utime: utime_us, - tms_stime: stime_us, - tms_cutime: utime_us, - tms_cstime: stime_us, - } - } + *tms.get(current().task_ext())? = Tms { + tms_utime: utime_us, + tms_stime: stime_us, + tms_cutime: utime_us, + tms_cstime: stime_us, + }; Ok(nanos_to_ticks(monotonic_time_nanos()) as _) } diff --git a/src/task.rs b/src/task.rs index 01e2e0403..e5ba6ad32 100644 --- a/src/task.rs +++ b/src/task.rs @@ -6,6 +6,7 @@ use alloc::{ use arceos_posix_api::FD_TABLE; use axerrno::{AxError, AxResult}; use axfs::{CURRENT_DIR, CURRENT_DIR_PATH}; +use axptr::AddrSpaceProvider; use core::{ alloc::Layout, cell::UnsafeCell, @@ -210,6 +211,12 @@ impl TaskExt { } } +impl AddrSpaceProvider for &TaskExt { + fn with_addr_space(&mut self, f: impl FnOnce(&mut AddrSpace) -> R) -> R { + f(&mut self.aspace.lock()) + } +} + struct AxNamespaceImpl; #[crate_interface::impl_interface] impl AxNamespaceIf for AxNamespaceImpl { @@ -294,7 +301,7 @@ pub fn read_trapframe_from_kstack(kstack_top: usize) -> TrapFrame { unsafe { *trap_frame_ptr } } -pub fn wait_pid(pid: i32, exit_code_ptr: *mut i32) -> Result { +pub fn wait_pid(pid: i32, exit_code_ptr: Option<&mut i32>) -> Result { let curr_task = current(); let mut exit_task_id: usize = 0; let mut answer_id: u64 = 0; @@ -316,10 +323,8 @@ pub fn wait_pid(pid: i32, exit_code_ptr: *mut i32) -> Result { exit_code ); exit_task_id = index; - if !exit_code_ptr.is_null() { - unsafe { - *exit_code_ptr = exit_code << 8; - } + if let Some(exit_code_ptr) = exit_code_ptr { + *exit_code_ptr = exit_code << 8; } answer_id = child.id().as_u64(); break; @@ -333,10 +338,8 @@ pub fn wait_pid(pid: i32, exit_code_ptr: *mut i32) -> Result { exit_code ); exit_task_id = index; - if !exit_code_ptr.is_null() { - unsafe { - *exit_code_ptr = exit_code << 8; - } + if let Some(exit_code_ptr) = exit_code_ptr { + *exit_code_ptr = exit_code << 8; } answer_id = child.id().as_u64(); } else { @@ -415,3 +418,7 @@ pub fn time_stat_output() -> (usize, usize, usize, usize) { stime_ns / NANOS_PER_MICROS as usize, ) } + +pub fn exit(exit_code: i32) -> ! { + axtask::exit(exit_code) +} From 84b670a14d02b5c35543bf3b8d16a209c42a38ca Mon Sep 17 00:00:00 2001 From: mivik Date: Tue, 1 Apr 2025 19:05:13 +0800 Subject: [PATCH 13/16] fix: signal libc test --- apps/libc/expect_off.out | 1 + 1 file changed, 1 insertion(+) diff --git a/apps/libc/expect_off.out b/apps/libc/expect_off.out index 4e86961f3..f6ea61e8e 100644 --- a/apps/libc/expect_off.out +++ b/apps/libc/expect_off.out @@ -22,3 +22,4 @@ test_sigwait ok3 test_sigsuspend ok1 test_sigsuspend ok2 test_sigsuspend ok3 +test_pause ok From 9ffe77f4d3cfaa40f0b6dacf3c4e7d660dcf5b0f Mon Sep 17 00:00:00 2001 From: mivik Date: Tue, 1 Apr 2025 19:06:35 +0800 Subject: [PATCH 14/16] feat: update axptr version --- Cargo.lock | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Cargo.lock b/Cargo.lock index 8303133ce..85e8841b8 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -433,7 +433,7 @@ dependencies = [ [[package]] name = "axptr" version = "0.1.0" -source = "git+https://github.com/Starry-OS/axptr.git#df2459f658316618b787af09b205a5f509352dc7" +source = "git+https://github.com/Starry-OS/axptr.git#d5f3a2939097f9059362634f78e616c18133605d" dependencies = [ "axerrno", "axhal", From 875cb5113b7704cf9894bea135cf3926525c992f Mon Sep 17 00:00:00 2001 From: mivik Date: Tue, 1 Apr 2025 19:20:25 +0800 Subject: [PATCH 15/16] feat: partially move signal to axsignal --- Cargo.lock | 15 ++ Cargo.toml | 2 + apps/libc/expect_off.out | 1 - src/main.rs | 1 - src/signal/ctypes.rs | 191 ---------------------- src/signal/mod.rs | 279 --------------------------------- src/syscall_imp/mod.rs | 3 + src/syscall_imp/signal.rs | 36 ++++- src/syscall_imp/task/thread.rs | 1 + src/task.rs | 2 +- 10 files changed, 51 insertions(+), 480 deletions(-) delete mode 100644 src/signal/ctypes.rs delete mode 100644 src/signal/mod.rs diff --git a/Cargo.lock b/Cargo.lock index 85e8841b8..efe71994a 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -463,6 +463,20 @@ dependencies = [ "percpu", ] +[[package]] +name = "axsignal" +version = "0.1.0" +dependencies = [ + "arceos_posix_api", + "axconfig", + "axerrno", + "axhal", + "axptr", + "axtask", + "bitflags 2.9.0", + "log", +] + [[package]] name = "axstd" version = "0.1.0" @@ -1424,6 +1438,7 @@ dependencies = [ "axns", "axptr", "axruntime", + "axsignal", "axstd", "axsync", "axtask", diff --git a/Cargo.toml b/Cargo.toml index 6f8b86b09..a97ef4297 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -37,6 +37,8 @@ arceos_posix_api = { git = "https://github.com/oscomp/arceos.git", features = [" axns = { git = "https://github.com/oscomp/arceos.git", features = ["thread-local"] } axptr = { git = "https://github.com/Starry-OS/axptr.git" } +axsignal = { path = "/home/mivik/Projects/axsignal" } + [patch.crates-io] syscalls = { git = "https://github.com/jasonwhite/syscalls.git", rev = "92624de"} page_table_multiarch = { git = "https://github.com/Mivik/page_table_multiarch.git", rev = "19ededd" } diff --git a/apps/libc/expect_off.out b/apps/libc/expect_off.out index f6ea61e8e..4e86961f3 100644 --- a/apps/libc/expect_off.out +++ b/apps/libc/expect_off.out @@ -22,4 +22,3 @@ test_sigwait ok3 test_sigsuspend ok1 test_sigsuspend ok2 test_sigsuspend ok3 -test_pause ok diff --git a/src/main.rs b/src/main.rs index 8ca77e850..b0b5bbf41 100644 --- a/src/main.rs +++ b/src/main.rs @@ -9,7 +9,6 @@ extern crate alloc; mod ctypes; mod mm; -mod signal; mod syscall_imp; mod task; diff --git a/src/signal/ctypes.rs b/src/signal/ctypes.rs deleted file mode 100644 index 4c975a911..000000000 --- a/src/signal/ctypes.rs +++ /dev/null @@ -1,191 +0,0 @@ -use core::{mem, ops::Not}; - -use arceos_posix_api as api; -use axerrno::LinuxError; -use bitflags::bitflags; - -bitflags! { - #[derive(Default, Debug)] - pub struct SignalActionFlags: u32 { - const SIGINFO = 4; - const NODEFER = 0x40000000; - const RESTORER = 0x04000000; - } -} - -/// Signal set. Corresponds to `struct sigset_t` in libc. -/// -/// Currently we only support 32 standard signals. -#[derive(Default, Clone, Copy)] -#[repr(transparent)] -pub struct SignalSet { - pub bits: [u32; 2], -} -impl SignalSet { - pub fn add(&mut self, signal: u32) -> bool { - if !(1..32).contains(&signal) { - return false; - } - let bit = 1 << (signal - 1); - if self.bits[0] & bit != 0 { - return false; - } - self.bits[0] |= bit; - true - } - pub fn remove(&mut self, signal: u32) -> bool { - if !(1..32).contains(&signal) { - return false; - } - let bit = 1 << (signal - 1); - if self.bits[0] & bit == 0 { - return false; - } - self.bits[0] &= !bit; - true - } - - pub fn has(&self, signal: u32) -> bool { - (1..32).contains(&signal) && (self.bits[0] & (1 << (signal - 1))) != 0 - } - - pub fn add_from(&mut self, other: &SignalSet) { - self.bits[0] |= other.bits[0]; - self.bits[1] |= other.bits[1]; - } - pub fn remove_from(&mut self, other: &SignalSet) { - self.bits[0] &= !other.bits[0]; - self.bits[1] &= !other.bits[1]; - } - - /// Dequeue the a signal in `mask` from this set, if any. - pub fn dequeue(&mut self, mask: &SignalSet) -> Option { - let bits = self.bits[0] & mask.bits[0]; - if bits == 0 { - None - } else { - let signal = bits.trailing_zeros(); - self.bits[0] &= !(1 << signal); - Some(signal + 1) - } - } -} - -impl Not for SignalSet { - type Output = Self; - - fn not(self) -> Self::Output { - Self { - bits: [!self.bits[0], !self.bits[1]], - } - } -} - -#[derive(Clone, Copy)] -#[repr(C)] -#[allow(non_camel_case_types)] -pub struct k_sigaction { - handler: Option, - flags: u32, - restorer: Option, - mask: SignalSet, -} - -#[derive(Default)] -pub enum SignalDisposition { - #[default] - /// Use the default signal action. - Default, - /// Ignore the signal. - Ignore, - /// Custom signal handler. - Handler(unsafe extern "C" fn(i32)), -} - -/// Signal action. Corresponds to `struct sigaction` in libc. -#[derive(Default)] -pub struct SignalAction { - pub flags: SignalActionFlags, - pub mask: SignalSet, - pub disposition: SignalDisposition, - pub restorer: Option, -} -impl SignalAction { - pub fn to_ctype(&self, dest: &mut k_sigaction) { - dest.flags = self.flags.bits() as _; - dest.mask = self.mask; - match &self.disposition { - SignalDisposition::Default => { - dest.handler = None; - } - SignalDisposition::Ignore => { - // SAFETY: SIG_IGN is 1 - dest.handler = - Some(unsafe { mem::transmute::(1) }); - } - SignalDisposition::Handler(handler) => { - dest.handler = Some(*handler); - } - } - dest.restorer = self.restorer; - } -} - -impl TryFrom for SignalAction { - type Error = LinuxError; - - fn try_from(value: k_sigaction) -> Result { - let Some(flags) = SignalActionFlags::from_bits(value.flags) else { - warn!("unrecognized signal flags: {}", value.flags); - return Err(LinuxError::EINVAL); - }; - let disposition = { - match value.handler { - None => { - // SIG_DFL - SignalDisposition::Default - } - Some(h) if h as usize == 1 => { - // SIG_IGN - SignalDisposition::Ignore - } - Some(h) => { - // Custom signal handler - SignalDisposition::Handler(h) - } - } - }; - Ok(SignalAction { - flags, - mask: value.mask, - disposition, - restorer: Some( - // SAFETY: `axconfig::plat::SIGNAL_TRAMPOLINE` is a valid function pointer - value.restorer.unwrap_or_else(|| unsafe { - mem::transmute(axconfig::plat::SIGNAL_TRAMPOLINE) - }), - ), - }) - } -} - -/// Signal information. Corresponds to `struct siginfo_t` in libc. -#[derive(Default, Clone)] -#[repr(transparent)] -pub struct SignalInfo(pub api::ctypes::siginfo_t); -impl SignalInfo { - pub const SI_USER: u32 = 0; - - pub fn new(signo: u32, code: u32) -> Self { - Self(api::ctypes::siginfo_t { - si_signo: signo as _, - si_errno: 0, - si_code: code as _, - ..Default::default() - }) - } - - pub fn signo(&self) -> u32 { - self.0.si_signo as u32 - } -} diff --git a/src/signal/mod.rs b/src/signal/mod.rs deleted file mode 100644 index 9d7af0d5a..000000000 --- a/src/signal/mod.rs +++ /dev/null @@ -1,279 +0,0 @@ -pub mod ctypes; - -use core::alloc::Layout; - -use alloc::sync::Arc; -use axhal::{arch::TrapFrame, trap::POST_TRAP}; -use axptr::{UserConstPtr, UserPtr}; -use axtask::{TaskExtRef, WaitQueue, current}; -use ctypes::{SignalAction, SignalActionFlags, SignalDisposition, SignalInfo, SignalSet}; -use linkme::distributed_slice as register_trap_handler; - -pub const SIGKILL: u32 = 9; -pub const SIGSTOP: u32 = 19; - -#[derive(Debug)] -enum DefaultSignalAction { - /// Terminate the process. - Terminate, - - /// Ignore the signal. - Ignore, - - /// Terminate the process and generate a core dump. - CoreDump, - - /// Stop the process. - Stop, - - /// Continue the process if stopped. - Continue, -} -const DEFAULT_ACTIONS: [DefaultSignalAction; 32] = [ - // Unspecified - DefaultSignalAction::Ignore, - // SIGHUP - DefaultSignalAction::Terminate, - // SIGINT - DefaultSignalAction::Terminate, - // SIGQUIT - DefaultSignalAction::CoreDump, - // SIGILL - DefaultSignalAction::CoreDump, - // SIGTRAP - DefaultSignalAction::CoreDump, - // SIGABRT - DefaultSignalAction::CoreDump, - // SIGBUS - DefaultSignalAction::CoreDump, - // SIGFPE - DefaultSignalAction::CoreDump, - // SIGKILL - DefaultSignalAction::Terminate, - // SIGUSR1 - DefaultSignalAction::Terminate, - // SIGSEGV - DefaultSignalAction::CoreDump, - // SIGUSR2 - DefaultSignalAction::Terminate, - // SIGPIPE - DefaultSignalAction::Terminate, - // SIGALRM - DefaultSignalAction::Terminate, - // SIGTERM - DefaultSignalAction::Terminate, - // SIGSTKFLT - DefaultSignalAction::Terminate, - // SIGCHLD - DefaultSignalAction::Ignore, - // SIGCONT - DefaultSignalAction::Continue, - // SIGSTOP - DefaultSignalAction::Stop, - // SIGTSTP - DefaultSignalAction::Stop, - // SIGTTIN - DefaultSignalAction::Stop, - // SIGTTOU - DefaultSignalAction::Stop, - // SIGURG - DefaultSignalAction::Ignore, - // SIGXCPU - DefaultSignalAction::CoreDump, - // SIGXFSZ - DefaultSignalAction::CoreDump, - // SIGVTALRM - DefaultSignalAction::Terminate, - // SIGPROF - DefaultSignalAction::Terminate, - // SIGWINCH - DefaultSignalAction::Ignore, - // SIGIO - DefaultSignalAction::Terminate, - // SIGPWR - DefaultSignalAction::Terminate, - // SIGSYS - DefaultSignalAction::CoreDump, -]; - -/// Structure to manage signal handling in a task. -pub struct SignalManager { - /// The set of signals currently blocked from delivery. - pub blocked: SignalSet, - - /// The signal actions. - actions: [SignalAction; 32], - - /// The pending signals. - /// - /// Note that does not correspond to `pending signals` as described in - /// Linux. `Pending signals` in Linux refers to the signals that are - /// delivered but blocked from delivery, while `pending` here refers to any - /// signal that is delivered and not yet handled. - pub pending: SignalSet, - pending_info: [Option; 32], - - /// The signals the task is currently waiting for. Used by - /// `sys_rt_sigtimedwait`. - pub waiting: SignalSet, - pub wq: Arc, - - /// If this is true, no more custom signal handler function should be - /// written to the trap frame, until it is set back to false at the end of - /// [`post_trap_callback`]. - pub prevent_signal_handling: bool, -} -impl SignalManager { - pub fn new() -> Self { - Self { - blocked: SignalSet::default(), - actions: Default::default(), - - pending: SignalSet::default(), - pending_info: Default::default(), - - waiting: SignalSet::default(), - wq: Arc::new(WaitQueue::new()), - - prevent_signal_handling: false, - } - } - - pub fn send_signal(&mut self, sig: SignalInfo) -> bool { - let signo = sig.signo(); - if !self.pending.add(signo) { - return false; - } - self.pending_info[signo as usize] = Some(sig); - if self.waiting.has(signo) { - self.wq.notify_one(false); - } - true - } - - pub fn set_action(&mut self, signo: u32, action: SignalAction) { - self.actions[signo as usize] = action; - } - pub fn action(&self, signo: u32) -> &SignalAction { - &self.actions[signo as usize] - } - - /// Dequeue the next pending signal contained in `mask`, if any. - pub fn dequeue_signal_in(&mut self, mask: &SignalSet) -> Option { - self.pending - .dequeue(mask) - .and_then(|signo| self.pending_info[signo as usize].take()) - } - - /// Dequeue the next non-blocked pending signal. - pub fn dequeue_signal(&mut self) -> Option { - self.dequeue_signal_in(&!self.blocked) - } - - /// Run the signal handler for the given signal. - /// - /// Returns `true` if the process should be terminated or a signal handler - /// should be executed. - pub fn run_action(&mut self, tf: &mut TrapFrame, sig: SignalInfo) -> bool { - let signo = sig.signo(); - info!("Handle signal: {}", signo); - let action = &self.actions[signo as usize]; - match action.disposition { - SignalDisposition::Default => match DEFAULT_ACTIONS[signo as usize] { - DefaultSignalAction::Terminate => crate::task::exit(128 + signo as i32), - DefaultSignalAction::CoreDump => { - warn!("Core dump not implemented"); - crate::task::exit(128 + signo as i32); - } - DefaultSignalAction::Stop => { - warn!("Stop not implemented"); - crate::task::exit(-1); - } - DefaultSignalAction::Ignore => false, - DefaultSignalAction::Continue => { - warn!("Continue not implemented"); - true - } - }, - SignalDisposition::Ignore => false, - SignalDisposition::Handler(handler) => { - let layout = Layout::new::(); - let aligned_sp = (tf.sp() - layout.size()) & !(layout.align() - 1); - - let mut frame_ptr: UserPtr = aligned_sp.into(); - let frame = frame_ptr - .get(current().task_ext()) - .expect("invalid frame ptr"); - - *frame = SignalFrame { - tf: *tf, - blocked: self.blocked, - siginfo: sig, - }; - - tf.set_ip(handler as _); - tf.set_sp(aligned_sp); - tf.set_arg0(signo as _); - tf.set_arg1(&frame.siginfo as *const _ as _); - tf.set_arg2(&frame as *const _ as _); - - let restorer = action.restorer.map_or(0, |f| f as _); - #[cfg(target_arch = "x86_64")] - tf.write_ra(restorer); - #[cfg(not(target_arch = "x86_64"))] - tf.set_ra(restorer); - - let mut mask = action.mask; - if !action.flags.contains(SignalActionFlags::NODEFER) { - mask.add(signo); - } - self.blocked.add_from(&mask); - true - } - } - } -} - -pub struct SignalFrame { - tf: TrapFrame, - blocked: SignalSet, - siginfo: SignalInfo, -} - -#[register_trap_handler(POST_TRAP)] -fn post_trap_callback(tf: &mut TrapFrame, from_user: bool) { - if !from_user { - return; - } - - let curr = current(); - let mut sigman = curr.task_ext().signal.lock(); - if sigman.prevent_signal_handling { - sigman.prevent_signal_handling = false; - return; - } - while let Some(sig) = sigman.dequeue_signal() { - if sigman.run_action(tf, sig) { - break; - } - } -} - -pub fn sigreturn(tf: &mut TrapFrame) { - let frame_ptr: UserConstPtr = tf.sp().into(); - let frame = frame_ptr - .get(current().task_ext()) - .expect("invalid frame ptr"); - - *tf = frame.tf; - #[cfg(any( - target_arch = "riscv32", - target_arch = "riscv64", - target_arch = "loongarch64" - ))] - tf.set_ip(tf.ip() - 4); - - let curr = current(); - let mut sigman = curr.task_ext().signal.lock(); - sigman.blocked = frame.blocked; -} diff --git a/src/syscall_imp/mod.rs b/src/syscall_imp/mod.rs index 08225ba79..264405543 100644 --- a/src/syscall_imp/mod.rs +++ b/src/syscall_imp/mod.rs @@ -141,6 +141,9 @@ fn handle_syscall(tf: &mut TrapFrame, syscall_num: usize) -> isize { tf.arg3() as _, ), Sysno::rt_sigsuspend => sys_rt_sigsuspend(tf, tf.arg0().into(), tf.arg1() as _), + Sysno::gettid => Ok(1), + +Sysno::prlimit64 => Ok(0), _ => { warn!("Unimplemented syscall: {}", syscall_num); crate::task::exit(LinuxError::ENOSYS as _) diff --git a/src/syscall_imp/signal.rs b/src/syscall_imp/signal.rs index 797d6540b..fcfee4769 100644 --- a/src/syscall_imp/signal.rs +++ b/src/syscall_imp/signal.rs @@ -2,15 +2,35 @@ use core::time::Duration; use arceos_posix_api as api; use axerrno::{LinuxError, LinuxResult}; -use axhal::arch::TrapFrame; +use axhal::{ + arch::TrapFrame, + trap::{POST_TRAP, register_trap_handler}, +}; use axptr::{UserConstPtr, UserPtr}; -use axtask::{TaskExtRef, current}; - -use crate::signal::{ +use axsignal::{ SIGKILL, SIGSTOP, ctypes::{SignalInfo, SignalSet, k_sigaction}, - sigreturn, }; +use axtask::{TaskExtRef, current}; + +#[register_trap_handler(POST_TRAP)] +fn post_trap_callback(tf: &mut TrapFrame, from_user: bool) { + if !from_user { + return; + } + + let curr = current(); + let mut sigman = curr.task_ext().signal.lock(); + if sigman.prevent_signal_handling { + sigman.prevent_signal_handling = false; + return; + } + while let Some(sig) = sigman.dequeue_signal() { + if sigman.run_action(tf, sig) { + break; + } + } +} fn check_sigset_size(size: usize) -> LinuxResult<()> { if size != size_of::() { @@ -62,7 +82,7 @@ pub fn sys_rt_sigaction( if !(1..32).contains(&signum) { return Err(LinuxError::EINVAL); } - if signum == crate::signal::SIGKILL || signum == crate::signal::SIGSTOP { + if signum == SIGKILL || signum == SIGSTOP { return Err(LinuxError::EINVAL); } @@ -94,7 +114,9 @@ pub fn sys_rt_sigpending(mut set: UserPtr, sigsetsize: usize) -> Linu } pub fn sys_rt_sigreturn(tf: &mut TrapFrame) -> LinuxResult { - sigreturn(tf); + let curr = current(); + let mut sigman = curr.task_ext().signal.lock(); + sigman.restore(tf, curr.task_ext()); Ok(tf.retval() as _) } diff --git a/src/syscall_imp/task/thread.rs b/src/syscall_imp/task/thread.rs index 7e0659048..ea69ca99c 100644 --- a/src/syscall_imp/task/thread.rs +++ b/src/syscall_imp/task/thread.rs @@ -199,6 +199,7 @@ pub fn sys_execve( .map(Into::into) }) .collect::, _>>()?; + drop(aspace); info!( "execve: path: {:?}, args: {:?}, envs: {:?}", diff --git a/src/task.rs b/src/task.rs index e5ba6ad32..846e24030 100644 --- a/src/task.rs +++ b/src/task.rs @@ -7,6 +7,7 @@ use arceos_posix_api::FD_TABLE; use axerrno::{AxError, AxResult}; use axfs::{CURRENT_DIR, CURRENT_DIR_PATH}; use axptr::AddrSpaceProvider; +use axsignal::SignalManager; use core::{ alloc::Layout, cell::UnsafeCell, @@ -18,7 +19,6 @@ use spin::Once; use crate::{ copy_from_kernel, ctypes::{CloneFlags, TimeStat, WaitStatus}, - signal::SignalManager, }; use axhal::{ arch::{TrapFrame, UspaceContext}, From 1dc6467007aa15aea63a3d77fd0078f4fbad06ec Mon Sep 17 00:00:00 2001 From: mivik Date: Fri, 4 Apr 2025 23:21:30 +0800 Subject: [PATCH 16/16] fix: axsignal path --- Cargo.lock | 1 + Cargo.toml | 3 +-- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index efe71994a..d9bb74acf 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -466,6 +466,7 @@ dependencies = [ [[package]] name = "axsignal" version = "0.1.0" +source = "git+https://github.com/Starry-OS/axsignal.git#813862199c755fad26494e46da84bd9a22e74731" dependencies = [ "arceos_posix_api", "axconfig", diff --git a/Cargo.toml b/Cargo.toml index a97ef4297..d1d051f0a 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -36,8 +36,7 @@ axruntime = { git = "https://github.com/oscomp/arceos.git", features = ["multita arceos_posix_api = { git = "https://github.com/oscomp/arceos.git", features = ["uspace", "smp", "irq", "fs", "multitask", "net", "pipe", "select", "epoll"] } axns = { git = "https://github.com/oscomp/arceos.git", features = ["thread-local"] } axptr = { git = "https://github.com/Starry-OS/axptr.git" } - -axsignal = { path = "/home/mivik/Projects/axsignal" } +axsignal = { git = "https://github.com/Starry-OS/axsignal.git" } [patch.crates-io] syscalls = { git = "https://github.com/jasonwhite/syscalls.git", rev = "92624de"}