diff --git a/Cargo.lock b/Cargo.lock index a194f3f00..ef09b957e 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -416,6 +416,19 @@ dependencies = [ "lazyinit", ] +[[package]] +name = "axptr" +version = "0.1.0" +source = "git+https://github.com/Starry-OS/axptr.git#d5f3a2939097f9059362634f78e616c18133605d" +dependencies = [ + "axerrno", + "axhal", + "axmm", + "axtask", + "memory_addr", + "percpu", +] + [[package]] name = "axruntime" version = "0.1.0" @@ -436,6 +449,21 @@ dependencies = [ "percpu", ] +[[package]] +name = "axsignal" +version = "0.1.0" +source = "git+https://github.com/Starry-OS/axsignal.git#e5b0245eb2bc98f5978a7aacef70c95b7ce187fe" +dependencies = [ + "arceos_posix_api", + "axconfig", + "axerrno", + "axhal", + "axptr", + "axtask", + "bitflags 2.9.0", + "log", +] + [[package]] name = "axsync" version = "0.1.0" @@ -988,22 +1016,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" @@ -1414,10 +1426,12 @@ dependencies = [ "axfs", "axhal", "axlog", + "axptr", + "axsignal", "axsync", "axtask", "bitflags 2.9.0", - "macro_rules_attribute", + "linkme", "memory_addr", "num_enum", "starry-core", @@ -1437,6 +1451,8 @@ dependencies = [ "axlog", "axmm", "axns", + "axptr", + "axsignal", "axsync", "axtask", "bitflags 2.9.0", diff --git a/Cargo.toml b/Cargo.toml index 74dd1d4ca..044166f21 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -37,6 +37,9 @@ axns = { git = "https://github.com/oscomp/arceos.git", features = [ ] } axsync = { git = "https://github.com/oscomp/arceos.git" } axtask = { git = "https://github.com/oscomp/arceos.git" } +axptr = { git = "https://github.com/Starry-OS/axptr.git" } +axsignal = { git = "https://github.com/Starry-OS/axsignal.git" } + axerrno = "0.1" bitflags = "2.6" diff --git a/api/Cargo.toml b/api/Cargo.toml index 7b00aadcf..4a3cdc90a 100644 --- a/api/Cargo.toml +++ b/api/Cargo.toml @@ -14,14 +14,16 @@ axlog.workspace = true axsync.workspace = true axtask.workspace = true arceos_posix_api.workspace = true +axptr.workspace = true +axsignal.workspace = true axerrno.workspace = true bitflags.workspace = true memory_addr.workspace = true +linkme.workspace = true starry-core.workspace = true -macro_rules_attribute = "0.2" num_enum = { version = "0.7", default-features = false } static_assertions = "1.1" diff --git a/api/src/imp/fs/ctl.rs b/api/src/imp/fs/ctl.rs index 37dce1f3f..fb80d9c35 100644 --- a/api/src/imp/fs/ctl.rs +++ b/api/src/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_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 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/api/src/imp/fs/io.rs b/api/src/imp/fs/io.rs index 8be57b4b9..a97ed6821 100644 --- a/api/src/imp/fs/io.rs +++ b/api/src/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 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 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 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 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 fn sys_writev( @@ -20,8 +20,8 @@ pub 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 fn sys_openat( @@ -30,7 +30,7 @@ pub 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/api/src/imp/fs/mount.rs b/api/src/imp/fs/mount.rs index 0bbdbd899..0ac31c148 100644 --- a/api/src/imp/fs/mount.rs +++ b/api/src/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/api/src/imp/fs/pipe.rs b/api/src/imp/fs/pipe.rs index 323f2c1ec..ffc6c2e5f 100644 --- a/api/src/imp/fs/pipe.rs +++ b/api/src/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/api/src/imp/fs/stat.rs b/api/src/imp/fs/stat.rs index 989c6df52..0019b4838 100644 --- a/api/src/imp/fs/stat.rs +++ b/api/src/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_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/api/src/imp/mm/brk.rs b/api/src/imp/mm/brk.rs index 5cd337243..ae867a1cf 100644 --- a/api/src/imp/mm/brk.rs +++ b/api/src/imp/mm/brk.rs @@ -1,10 +1,6 @@ use axerrno::LinuxResult; use axtask::{TaskExtRef, current}; -use macro_rules_attribute::apply; -use crate::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/api/src/imp/mm/mmap.rs b/api/src/imp/mm/mmap.rs index 1f831f04f..a9a6b6820 100644 --- a/api/src/imp/mm/mmap.rs +++ b/api/src/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_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/api/src/imp/signal.rs b/api/src/imp/signal.rs index a24c420d9..fcfee4769 100644 --- a/api/src/imp/signal.rs +++ b/api/src/imp/signal.rs @@ -1,25 +1,271 @@ -use core::ffi::c_void; +use core::time::Duration; -use axerrno::LinuxResult; +use arceos_posix_api as api; +use axerrno::{LinuxError, LinuxResult}; +use axhal::{ + arch::TrapFrame, + trap::{POST_TRAP, register_trap_handler}, +}; +use axptr::{UserConstPtr, UserPtr}; +use axsignal::{ + SIGKILL, SIGSTOP, + ctypes::{SignalInfo, SignalSet, k_sigaction}, +}; +use axtask::{TaskExtRef, current}; -use crate::ptr::{UserConstPtr, UserPtr}; +#[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::() { + 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(mut oldset) = oldset.nullable() { + *oldset.get(curr.task_ext())? = sigman.blocked; + } + + 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, + _ => 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 == SIGKILL || signum == SIGSTOP { + return Err(LinuxError::EINVAL); + } + + let curr = current(); + let mut sigman = curr.task_ext().signal.lock(); + + 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() { + let act = act.get(curr.task_ext())?; + sigman.set_action(signum, (*act).try_into()?); + } + Ok(0) } + +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(); + + *set.get(curr.task_ext())? = sigman.pending; + + Ok(0) +} + +pub fn sys_rt_sigreturn(tf: &mut TrapFrame) -> LinuxResult { + let curr = current(); + let mut sigman = curr.task_ext().signal.lock(); + sigman.restore(tf, curr.task_ext()); + Ok(tf.retval() as _) +} + +pub fn sys_rt_sigtimedwait( + set: UserConstPtr, + info: UserPtr, + timeout: UserConstPtr, + sigsetsize: usize, +) -> LinuxResult { + check_sigset_size(sigsetsize)?; + + let curr = current(); + let mut set = *set.get(curr.task_ext())?; + + 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 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(mut info) = info.nullable() { + *info.get(curr.task_ext())? = 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(mut info) = info.nullable() { + *info.get(curr.task_ext())? = siginfo; + } + return Ok(0); + } + + // TODO: EINTR + + Err(LinuxError::EAGAIN) +} + +pub fn sys_rt_sigsuspend( + tf: &mut TrapFrame, + mut set: UserPtr, + sigsetsize: usize, +) -> LinuxResult { + check_sigset_size(sigsetsize)?; + + let curr = current(); + let set = set.get(curr.task_ext())?; + + 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); + } + 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 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 + 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/api/src/imp/sys.rs b/api/src/imp/sys.rs index ccfc15661..cc5df28ff 100644 --- a/api/src/imp/sys.rs +++ b/api/src/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/api/src/imp/task/schedule.rs b/api/src/imp/task/schedule.rs index 692311ed5..e4671325c 100644 --- a/api/src/imp/task/schedule.rs +++ b/api/src/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/api/src/imp/task/thread.rs b/api/src/imp/task/thread.rs index 990e07f14..341e89553 100644 --- a/api/src/imp/task/thread.rs +++ b/api/src/imp/task/thread.rs @@ -1,18 +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 starry_core::{ ctypes::{WaitFlags, WaitStatus}, - task::{exec, wait_pid}, -}; - -use crate::{ - ptr::{PtrWrapper, UserConstPtr, UserPtr}, - syscall_instrument, + task::{exec, exit, wait_pid}, }; /// ARCH_PRCTL codes @@ -36,12 +31,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 _) } @@ -57,27 +50,25 @@ 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); + exit(status); } pub fn sys_exit_group(status: i32) -> ! { warn!("Temporarily replace sys_exit_group with sys_exit"); - axtask::exit(status); + 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) } #[cfg(target_arch = "x86_64")] -#[apply(syscall_instrument)] pub fn sys_arch_prctl(code: i32, addr: UserPtr) -> LinuxResult { use axerrno::LinuxError; match ArchPrctlCode::try_from(code).map_err(|_| LinuxError::EINVAL)? { @@ -113,7 +104,6 @@ pub fn sys_arch_prctl(code: i32, addr: UserPtr) -> LinuxResult { } } -#[apply(syscall_instrument)] pub fn sys_clone( flags: usize, user_stack: usize, @@ -121,6 +111,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; @@ -142,12 +136,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 = unsafe { 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); @@ -171,32 +170,34 @@ 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::, _>>()?; + drop(aspace); info!( "execve: path: {:?}, args: {:?}, envs: {:?}", diff --git a/api/src/imp/utils/time.rs b/api/src/imp/utils/time.rs index 4cda85e61..2c9d6faa2 100644 --- a/api/src/imp/utils/time.rs +++ b/api/src/imp/utils/time.rs @@ -1,27 +1,29 @@ use arceos_posix_api::{self as api, ctypes::timeval}; use axerrno::LinuxResult; use axhal::time::{monotonic_time_nanos, nanos_to_ticks}; -use starry_core::{ctypes::Tms, task::time_stat_output}; +use axptr::UserPtr; +use axtask::{TaskExtRef, current}; -use crate::ptr::{PtrWrapper, UserPtr}; +use starry_core::{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/api/src/lib.rs b/api/src/lib.rs index c3f3ec9cf..dececa773 100644 --- a/api/src/lib.rs +++ b/api/src/lib.rs @@ -5,38 +5,5 @@ extern crate axlog; extern crate alloc; mod imp; -mod ptr; pub use imp::*; - -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; diff --git a/api/src/ptr.rs b/api/src/ptr.rs deleted file mode 100644 index 2ad65ad87..000000000 --- a/api/src/ptr.rs +++ /dev/null @@ -1,238 +0,0 @@ -use core::{alloc::Layout, ffi::c_char, mem, slice, str}; - -use axerrno::{LinuxError, LinuxResult}; -use axhal::paging::MappingFlags; -use axtask::{TaskExtRef, current}; -use memory_addr::{MemoryAddr, PAGE_SIZE_4K, VirtAddr, VirtAddrRange}; -use starry_core::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/apps/libc/c/signal/signal.c b/apps/libc/c/signal/signal.c new file mode 100644 index 000000000..9d0b65105 --- /dev/null +++ b/apps/libc/c/signal/signal.c @@ -0,0 +1,181 @@ +#include +#include +#include +#include +#include +#include + +void test_term() { + if (fork() == 0) { + kill(getpid(), SIGTERM); + while (1) + ; + } + wait(NULL); + puts("test_term ok"); +} + +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() { + struct sigaction sa = {0}; + sa.sa_handler = signal_handler; + sa.sa_flags = 0; + sigaction(SIGTERM, &sa, NULL); + kill(0, SIGTERM); + puts("test_sigaction ok1"); + + sa.sa_handler = (void (*)(int))1; + sigaction(SIGTERM, &sa, NULL); + kill(0, SIGTERM); + puts("test_sigaction ok2"); + + sa.sa_handler = (void (*)(int))0; + sigaction(SIGTERM, &sa, NULL); +} + +void 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("test_sigprocmask 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("test_sigprocmask ok2"); + } + + sa.sa_handler = (void (*)(int))0; + sigaction(SIGTERM, &sa, NULL); +} + +void test_sigkill_stop() { + struct sigaction sa = {0}; + sa.sa_handler = signal_handler; + sa.sa_flags = 0; + if (sigaction(SIGKILL, &sa, NULL) < 0) { + puts("test_sigkill_stop ok1"); + } + if (sigaction(SIGSTOP, &sa, NULL) < 0) { + puts("test_sigkill_stop ok2"); + } +} + +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_sigaction(); + test_sigprocmask(); + test_sigkill_stop(); + test_sigwait(); + test_sigsuspend(); + 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..4e86961f3 100644 --- a/apps/libc/expect_off.out +++ b/apps/libc/expect_off.out @@ -3,5 +3,22 @@ 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 ok +Received signal 15, count=1 +End, count=1 +Received signal 15, count=2 +test_sigaction ok1 +test_sigaction ok2 +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/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/core/Cargo.toml b/core/Cargo.toml index 84ef8a95d..50962be49 100644 --- a/core/Cargo.toml +++ b/core/Cargo.toml @@ -16,6 +16,8 @@ axns.workspace = true axsync.workspace = true axtask.workspace = true arceos_posix_api.workspace = true +axsignal.workspace = true +axptr.workspace = true axerrno.workspace = true bitflags.workspace = true diff --git a/core/src/mm.rs b/core/src/mm.rs index 944b0e953..b9d4e1cae 100644 --- a/core/src/mm.rs +++ b/core/src/mm.rs @@ -170,27 +170,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; } @@ -205,7 +191,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/core/src/task.rs b/core/src/task.rs index a761ab7c1..63336a5e9 100644 --- a/core/src/task.rs +++ b/core/src/task.rs @@ -12,16 +12,19 @@ use alloc::{ use arceos_posix_api::FD_TABLE; use axerrno::{AxError, AxResult}; use axfs::{CURRENT_DIR, CURRENT_DIR_PATH}; +use axptr::AddrSpaceProvider; +use axsignal::SignalManager; +use memory_addr::VirtAddrRange; +use spin::Once; + use axhal::{ arch::{TrapFrame, UspaceContext}, time::{NANOS_PER_MICROS, NANOS_PER_SEC, monotonic_time_nanos}, }; use axmm::{AddrSpace, kernel_aspace}; use axns::{AxNamespace, AxNamespaceIf}; -use axsync::Mutex; +use axsync::{Mutex, spin::SpinNoIrq}; use axtask::{AxTaskRef, TaskExtRef, TaskInner, current}; -use memory_addr::VirtAddrRange; -use spin::Once; use crate::{ ctypes::{CloneFlags, TimeStat, WaitStatus}, @@ -54,6 +57,8 @@ pub struct TaskExt { pub heap_bottom: AtomicU64, /// The user heap top pub heap_top: AtomicU64, + // The signal manager + pub signal: SpinNoIrq, } impl TaskExt { @@ -74,6 +79,7 @@ impl TaskExt { time: TimeStat::new().into(), heap_bottom: AtomicU64::new(heap_bottom), heap_top: AtomicU64::new(heap_bottom), + signal: SpinNoIrq::new(SignalManager::new()), } } @@ -102,6 +108,10 @@ impl TaskExt { current().id_name(), axconfig::plat::KERNEL_STACK_SIZE, ); + #[cfg(target_arch = "x86_64")] + 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(); @@ -115,11 +125,12 @@ impl TaskExt { .ctx_mut() .set_tls(axhal::arch::read_thread_pointer().into()); - 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); } + let mut new_uctx = UspaceContext::from(&trap_frame); // Skip current instruction #[cfg(any(target_arch = "riscv64", target_arch = "loongarch64"))] { @@ -208,6 +219,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 { @@ -292,10 +309,7 @@ pub fn read_trapframe_from_kstack(kstack_top: usize) -> TrapFrame { unsafe { *trap_frame_ptr } } -/// # Safety -/// -/// The caller must ensure that the pointer is valid and properly aligned if it's not null. -pub unsafe 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; @@ -317,10 +331,8 @@ pub unsafe fn wait_pid(pid: i32, exit_code_ptr: *mut i32) -> Result Result (usize, usize, usize, usize) { stime_ns / NANOS_PER_MICROS as usize, ) } + +pub fn exit(exit_code: i32) -> ! { + axtask::exit(exit_code) +} diff --git a/src/syscall.rs b/src/syscall.rs index 1a946a8f2..044ac475b 100644 --- a/src/syscall.rs +++ b/src/syscall.rs @@ -4,12 +4,11 @@ use axhal::{ trap::{SYSCALL, register_trap_handler}, }; use starry_api::*; -use starry_core::task::{time_stat_from_kernel_to_user, time_stat_from_user_to_kernel}; +use starry_core::task::{exit, time_stat_from_kernel_to_user, time_stat_from_user_to_kernel}; use syscalls::Sysno; #[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 _), @@ -41,6 +40,8 @@ fn handle_syscall(tf: &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 _), @@ -105,6 +106,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(), @@ -117,9 +119,21 @@ 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 _), + 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 _), + Sysno::gettid => Ok(1), + + Sysno::prlimit64 => Ok(0), _ => { warn!("Unimplemented syscall: {}", syscall_num); - axtask::exit(LinuxError::ENOSYS as _) + exit(LinuxError::ENOSYS as _) } }; let ans = result.unwrap_or_else(|err| -err.code() as _);