From c98b9d360f765acab999ce66c16737c9f0adbd1c Mon Sep 17 00:00:00 2001 From: Jianfeng Jiang Date: Tue, 8 Nov 2022 17:39:49 +0800 Subject: [PATCH] improve signal implementation to pass occlum signal test --- .gitattributes | 4 +- src/kxos-frame/src/config.rs | 2 +- src/kxos-frame/src/cpu.rs | 8 +- src/kxos-frame/src/log.rs | 1 + src/kxos-pci/Cargo.toml | 2 +- src/kxos-pci/src/lib.rs | 2 + src/kxos-pci/src/msix.rs | 2 +- src/kxos-std/src/memory/mod.rs | 47 +-- src/kxos-std/src/prelude.rs | 2 +- src/kxos-std/src/process/exception.rs | 2 +- src/kxos-std/src/process/mod.rs | 9 +- src/kxos-std/src/process/signal/c_types.rs | 83 +++++ src/kxos-std/src/process/signal/mod.rs | 107 ++++--- src/kxos-std/src/process/signal/sig_action.rs | 9 + src/kxos-std/src/process/signal/sig_mask.rs | 8 + .../src/process/signal/signals/fault.rs | 7 + .../src/process/signal/signals/kernel.rs | 7 +- .../src/process/signal/signals/mod.rs | 4 +- .../src/process/signal/signals/user.rs | 26 +- src/kxos-std/src/process/task.rs | 6 +- src/kxos-std/src/syscall/access.rs | 2 +- src/kxos-std/src/syscall/execve.rs | 2 +- src/kxos-std/src/syscall/futex.rs | 2 +- src/kxos-std/src/syscall/readlink.rs | 14 +- src/kxos-std/src/syscall/rt_sigaction.rs | 6 +- src/kxos-std/src/syscall/rt_sigreturn.rs | 11 +- src/kxos-std/src/syscall/tgkill.rs | 35 ++- src/kxos-std/src/syscall/uname.rs | 8 +- src/kxos-std/src/syscall/wait4.rs | 2 +- src/kxos-std/src/syscall/write.rs | 8 +- src/kxos-std/src/syscall/writev.rs | 10 +- src/kxos-std/src/user_apps.rs | 46 +-- src/kxos-user/signal_c/Makefile | 11 +- src/kxos-user/signal_c/divide_zero | 3 - src/kxos-user/signal_c/divide_zero.c | 7 - src/kxos-user/signal_c/sig_action | 3 - src/kxos-user/signal_c/sig_action.c | 29 -- src/kxos-user/signal_c/sig_procmask | 3 - src/kxos-user/signal_c/sig_procmask.c | 19 -- src/kxos-user/signal_c/signal_test | 3 + src/kxos-user/signal_c/signal_test.c | 285 ++++++++++++++++++ src/kxos-virtio/Cargo.toml | 3 +- src/kxos-virtio/src/block.rs | 1 - src/kxos-virtio/src/lib.rs | 4 +- src/kxos-virtio/src/queue.rs | 1 - 45 files changed, 636 insertions(+), 220 deletions(-) delete mode 100755 src/kxos-user/signal_c/divide_zero delete mode 100644 src/kxos-user/signal_c/divide_zero.c delete mode 100755 src/kxos-user/signal_c/sig_action delete mode 100644 src/kxos-user/signal_c/sig_action.c delete mode 100755 src/kxos-user/signal_c/sig_procmask delete mode 100644 src/kxos-user/signal_c/sig_procmask.c create mode 100755 src/kxos-user/signal_c/signal_test create mode 100644 src/kxos-user/signal_c/signal_test.c diff --git a/.gitattributes b/.gitattributes index 512faf1f9..9aecc4572 100644 --- a/.gitattributes +++ b/.gitattributes @@ -4,6 +4,4 @@ src/kxos-user/hello_c/hello filter=lfs diff=lfs merge=lfs -text src/kxos-user/execve/execve filter=lfs diff=lfs merge=lfs -text src/kxos-user/execve/hello filter=lfs diff=lfs merge=lfs -text src/kxos-user/fork_c/fork filter=lfs diff=lfs merge=lfs -text -src/kxos-user/signal_c/divide_zero filter=lfs diff=lfs merge=lfs -text -src/kxos-user/signal_c/sig_procmask filter=lfs diff=lfs merge=lfs -text -src/kxos-user/signal_c/sig_action filter=lfs diff=lfs merge=lfs -text \ No newline at end of file +src/kxos-user/signal_c/signal_test filter=lfs diff=lfs merge=lfs -text \ No newline at end of file diff --git a/src/kxos-frame/src/config.rs b/src/kxos-frame/src/config.rs index 99cb14c4a..f62b6c64a 100644 --- a/src/kxos-frame/src/config.rs +++ b/src/kxos-frame/src/config.rs @@ -15,4 +15,4 @@ pub const PAGE_SIZE_BITS: usize = 0xc; pub const KVA_START: usize = (usize::MAX) << PAGE_SIZE_BITS; -pub const DEFAULT_LOG_LEVEL: LogLevel = LogLevel::Info; +pub const DEFAULT_LOG_LEVEL: LogLevel = LogLevel::Close; diff --git a/src/kxos-frame/src/cpu.rs b/src/kxos-frame/src/cpu.rs index 89878205c..4e86db11f 100644 --- a/src/kxos-frame/src/cpu.rs +++ b/src/kxos-frame/src/cpu.rs @@ -1,6 +1,10 @@ //! CPU. -use crate::trap::{CalleeRegs, CallerRegs, SyscallFrame, TrapFrame}; +use crate::vm::Pod; +use crate::{ + impl_pod_for, + trap::{CalleeRegs, CallerRegs, SyscallFrame, TrapFrame}, +}; /// Defines a CPU-local variable. #[macro_export] @@ -65,6 +69,8 @@ pub struct GpRegs { pub rflag: u64, } +impl_pod_for!(GpRegs, TrapInformation, CpuContext, FpRegs); + impl From for CpuContext { fn from(syscall: SyscallFrame) -> Self { Self { diff --git a/src/kxos-frame/src/log.rs b/src/kxos-frame/src/log.rs index fe2f5825a..4ff1f49df 100644 --- a/src/kxos-frame/src/log.rs +++ b/src/kxos-frame/src/log.rs @@ -138,4 +138,5 @@ pub enum LogLevel { Info, Warn, Error, + Close, } diff --git a/src/kxos-pci/Cargo.toml b/src/kxos-pci/Cargo.toml index 610e79fd4..e936683aa 100644 --- a/src/kxos-pci/Cargo.toml +++ b/src/kxos-pci/Cargo.toml @@ -10,7 +10,7 @@ bitflags = "1.3" spin = "0.9.4" kxos-frame = {path = "../kxos-frame"} kxos-util = {path="../kxos-util"} -kxos-frame-pod-derive= {path="../kxos-frame-pod-derive"} +kxos-frame-pod-derive = {path = "../kxos-frame-pod-derive"} [dependencies.lazy_static] version = "1.0" diff --git a/src/kxos-pci/src/lib.rs b/src/kxos-pci/src/lib.rs index 7ac9dcf62..70b3d1f61 100644 --- a/src/kxos-pci/src/lib.rs +++ b/src/kxos-pci/src/lib.rs @@ -7,6 +7,8 @@ pub mod msix; pub mod util; extern crate alloc; use kxos_frame::info; +#[macro_use] +extern crate kxos_frame_pod_derive; use alloc::{sync::Arc, vec::Vec}; use lazy_static::lazy_static; diff --git a/src/kxos-pci/src/msix.rs b/src/kxos-pci/src/msix.rs index d7accb32a..803e0291b 100644 --- a/src/kxos-pci/src/msix.rs +++ b/src/kxos-pci/src/msix.rs @@ -5,7 +5,7 @@ use kxos_frame_pod_derive::Pod; use super::capability::msix::CapabilityMSIXData; -use kxos_frame::{offset_of, vm::Pod, IrqAllocateHandle}; +use kxos_frame::{offset_of, IrqAllocateHandle}; use kxos_util::frame_ptr::InFramePtr; #[derive(Debug, Default)] diff --git a/src/kxos-std/src/memory/mod.rs b/src/kxos-std/src/memory/mod.rs index 0bba03709..496fc467f 100644 --- a/src/kxos-std/src/memory/mod.rs +++ b/src/kxos-std/src/memory/mod.rs @@ -6,37 +6,44 @@ pub mod vm_page; use crate::process::Process; /// copy bytes from user space of current process. The bytes len is the len of dest. -pub fn read_bytes_from_user(src: Vaddr, dest: &mut [u8]) { +pub fn read_bytes_from_user(src: Vaddr, dest: &mut [u8]) -> Result<()> { let current = Process::current(); - let vm_space = current - .vm_space() - .expect("[Internal error]Current should have vm space to copy bytes from user"); - vm_space.read_bytes(src, dest).expect("read bytes failed"); + let vm_space = current.vm_space().ok_or(Error::with_message( + Errno::ESRCH, + "[Internal error]Current should have vm space to copy bytes from user", + ))?; + vm_space.read_bytes(src, dest)?; + Ok(()) } /// copy val (Plain of Data type) from user space of current process. -pub fn read_val_from_user(src: Vaddr) -> T { +pub fn read_val_from_user(src: Vaddr) -> Result { let current = Process::current(); - let vm_space = current - .vm_space() - .expect("[Internal error]Current should have vm space to copy val from user"); - vm_space.read_val(src).expect("read val failed") + let vm_space = current.vm_space().ok_or(Error::with_message( + Errno::ESRCH, + "[Internal error]Current should have vm space to copy val from user", + ))?; + Ok(vm_space.read_val(src)?) } /// write bytes from user space of current process. The bytes len is the len of src. -pub fn write_bytes_to_user(dest: Vaddr, src: &[u8]) { +pub fn write_bytes_to_user(dest: Vaddr, src: &[u8]) -> Result<()> { let current = Process::current(); - let vm_space = current - .vm_space() - .expect("[Internal error]Current should have vm space to write bytes to user"); - vm_space.write_bytes(dest, src).expect("write bytes failed") + let vm_space = current.vm_space().ok_or(Error::with_message( + Errno::ESRCH, + "[Internal error]Current should have vm space to write bytes to user", + ))?; + vm_space.write_bytes(dest, src)?; + Ok(()) } /// write val (Plain of Data type) to user space of current process. -pub fn write_val_to_user(dest: Vaddr, val: &T) { +pub fn write_val_to_user(dest: Vaddr, val: &T) -> Result<()> { let current = Process::current(); - let vm_space = current - .vm_space() - .expect("[Internal error]Current should have vm space to write val to user"); - vm_space.write_val(dest, val).expect("write val failed"); + let vm_space = current.vm_space().ok_or(Error::with_message( + Errno::ESRCH, + "[Internal error]Current should have vm space to write val to user", + ))?; + vm_space.write_val(dest, val)?; + Ok(()) } diff --git a/src/kxos-std/src/prelude.rs b/src/kxos-std/src/prelude.rs index a05ad1e94..7a80a5708 100644 --- a/src/kxos-std/src/prelude.rs +++ b/src/kxos-std/src/prelude.rs @@ -13,7 +13,7 @@ pub(crate) use bitflags::bitflags; pub(crate) use core::ffi::CStr; pub(crate) use kxos_frame::config::PAGE_SIZE; pub(crate) use kxos_frame::vm::Vaddr; -pub(crate) use kxos_frame::{debug, error, info, trace, warn}; +pub(crate) use kxos_frame::{debug, error, info, print, println, trace, warn}; pub(crate) use spin::Mutex; #[macro_export] diff --git a/src/kxos-std/src/process/exception.rs b/src/kxos-std/src/process/exception.rs index 1c4b9b8fa..394b9820d 100644 --- a/src/kxos-std/src/process/exception.rs +++ b/src/kxos-std/src/process/exception.rs @@ -2,7 +2,7 @@ use kxos_frame::cpu::CpuContext; use crate::{prelude::*, process::signal::signals::fault::FaultSignal}; -/// We can't handle most exceptions, just send self a signal to force the process exit before return to user space. +/// We can't handle most exceptions, just send self a fault signal before return to user space. pub fn handle_exception(context: &mut CpuContext) { let trap_info = context.trap_information.clone(); let current = current!(); diff --git a/src/kxos-std/src/process/mod.rs b/src/kxos-std/src/process/mod.rs index 6e27949c8..c9003df73 100644 --- a/src/kxos-std/src/process/mod.rs +++ b/src/kxos-std/src/process/mod.rs @@ -14,7 +14,6 @@ use self::signal::sig_disposition::SigDispositions; use self::signal::sig_mask::SigMask; use self::signal::sig_queues::SigQueues; use self::signal::signals::kernel::KernelSignal; -use self::signal::SigContext; use self::status::ProcessStatus; use self::task::create_user_task_from_elf; @@ -65,8 +64,8 @@ pub struct Process { sig_queues: Mutex, /// Process-level sigmask sig_mask: Mutex, - /// Signal handler Context - sig_context: Mutex>, + /// Signal handler ucontext address + sig_context: Mutex>, } impl Process { @@ -119,7 +118,7 @@ impl Process { sig_dispositions: Mutex::new(sig_dispositions), sig_queues: Mutex::new(sig_queues), sig_mask: Mutex::new(sig_mask), - sig_context: Mutex::new(None), + sig_context: Mutex::new(VecDeque::new()), } } @@ -226,7 +225,7 @@ impl Process { &self.process_group } - pub fn sig_context(&self) -> &Mutex> { + pub fn sig_context(&self) -> &Mutex> { &self.sig_context } diff --git a/src/kxos-std/src/process/signal/c_types.rs b/src/kxos-std/src/process/signal/c_types.rs index d30bf3def..ad0a74af3 100644 --- a/src/kxos-std/src/process/signal/c_types.rs +++ b/src/kxos-std/src/process/signal/c_types.rs @@ -1,6 +1,12 @@ #![allow(non_camel_case_types)] +use core::mem; + +use kxos_frame::cpu::GpRegs; + use crate::prelude::*; +use super::sig_num::SigNum; + pub type sigset_t = u64; #[derive(Debug, Clone, Copy, Pod)] @@ -11,3 +17,80 @@ pub struct sigaction_t { pub restorer_ptr: Vaddr, pub mask: sigset_t, } + +#[derive(Debug, Clone, Copy, Pod)] +#[repr(C)] +pub struct siginfo_t { + pub si_signo: i32, + pub si_errno: i32, + pub si_code: i32, + _padding: i32, + /// siginfo_fields should be a union type ( See occlum definition ). But union type have unsafe interfaces. + /// Here we use a simple byte array. + pub siginfo_fields: [u8; 128 - mem::size_of::() * 4], +} + +impl siginfo_t { + pub fn new(num: SigNum, code: i32) -> Self { + let zero_fields = [0u8; 128 - mem::size_of::() * 4]; + siginfo_t { + si_signo: num.as_u8() as i32, + si_errno: 0, + si_code: code, + _padding: 0, + siginfo_fields: zero_fields, + } + } +} + +#[derive(Clone, Copy, Debug, Pod)] +#[repr(C)] +pub struct ucontext_t { + pub uc_flags: u64, + pub uc_link: Vaddr, // *mut ucontext_t + pub uc_stack: stack_t, + pub uc_mcontext: mcontext_t, + pub uc_sigmask: sigset_t, + pub fpregs: [u8; 64 * 8], //fxsave structure +} + +impl Default for ucontext_t { + fn default() -> Self { + Self { + uc_flags: Default::default(), + uc_link: Default::default(), + uc_stack: Default::default(), + uc_mcontext: Default::default(), + uc_sigmask: Default::default(), + fpregs: [0u8; 64 * 8], + } + } +} + +pub type stack_t = sigaltstack_t; + +#[derive(Debug, Clone, Copy, Pod, Default)] +#[repr(C)] +pub struct sigaltstack_t { + pub ss_sp: Vaddr, // *mut c_void + pub ss_flags: i32, + pub ss_size: usize, +} + +#[derive(Debug, Clone, Copy, Pod, Default)] +#[repr(C)] +pub struct mcontext_t { + pub inner: SignalCpuContext, + // TODO: the fields should be csgsfs, err, trapno, oldmask, and cr2 + _unused0: [u64; 5], + // TODO: this field should be `fpregs: fpregset_t,` + _unused1: usize, + _reserved: [u64; 8], +} + +#[derive(Debug, Clone, Copy, Pod, Default)] +pub struct SignalCpuContext { + pub gp_regs: GpRegs, + pub fpregs_on_heap: u64, + pub fpregs: Vaddr, // *mut FpRegs, +} diff --git a/src/kxos-std/src/process/signal/mod.rs b/src/kxos-std/src/process/signal/mod.rs index dcde0ced7..f38d70714 100644 --- a/src/kxos-std/src/process/signal/mod.rs +++ b/src/kxos-std/src/process/signal/mod.rs @@ -7,11 +7,16 @@ pub mod sig_num; pub mod sig_queues; pub mod signals; +use core::mem; + +use kxos_frame::AlignExt; use kxos_frame::{cpu::CpuContext, task::Task}; +use self::c_types::siginfo_t; use self::sig_mask::SigMask; use self::sig_num::SigNum; use crate::memory::{write_bytes_to_user, write_val_to_user}; +use crate::process::signal::c_types::ucontext_t; use crate::process::signal::sig_action::SigActionFlags; use crate::{ prelude::*, @@ -19,7 +24,7 @@ use crate::{ }; /// Handle pending signal for current process -pub fn handle_pending_signal(context: &mut CpuContext) { +pub fn handle_pending_signal(context: &mut CpuContext) -> Result<()> { let current = current!(); let pid = current.pid(); let process_name = current.filename().unwrap(); @@ -47,7 +52,8 @@ pub fn handle_pending_signal(context: &mut CpuContext) { restorer_addr, mask, context, - ), + signal.to_info(), + )?, SigAction::Dfl => { let sig_default_action = SigDefaultAction::from_signum(sig_num); debug!("sig_default_action: {:?}", sig_default_action); @@ -86,6 +92,7 @@ pub fn handle_pending_signal(context: &mut CpuContext) { } } } + Ok(()) } pub fn handle_user_signal_handler( @@ -93,32 +100,54 @@ pub fn handle_user_signal_handler( handler_addr: Vaddr, flags: SigActionFlags, restorer_addr: Vaddr, - mask: SigMask, + mut mask: SigMask, context: &mut CpuContext, -) { + sig_info: siginfo_t, +) -> Result<()> { debug!("sig_num = {:?}", sig_num); debug!("handler_addr = 0x{:x}", handler_addr); debug!("flags = {:?}", flags); debug!("restorer_addr = 0x{:x}", restorer_addr); - // FIXME: How to respect flags - if flags.intersects(!(SigActionFlags::SA_RESTART | SigActionFlags::SA_RESTORER)) { + // FIXME: How to respect flags? + if flags.contains_unsupported_flag() { panic!("Unsupported Signal flags"); } + if !flags.contains(SigActionFlags::SA_NODEFER) { + // add current signal to mask + let current_mask = SigMask::from(sig_num); + mask.block(current_mask.as_u64()); + } let current = current!(); // block signals in sigmask when running signal handler current.sig_mask().lock().block(mask.as_u64()); - // store context in current process - let sig_context = SigContext::new(context.clone(), mask); - *(current.sig_context().lock()) = Some(sig_context); - // set up signal stack in user stack + + // set up signal stack in user stack. // avoid corrupt user stack, we minus 128 first. let mut user_rsp = context.gp_regs.rsp; - // avoid corrupt user stack, we minus 128 first. user_rsp = user_rsp - 128; - // Copy the trampoline code. + + // 1. write siginfo_t + user_rsp = user_rsp - mem::size_of::() as u64; + write_val_to_user(user_rsp as _, &sig_info)?; + let siginfo_addr = user_rsp; + debug!("siginfo_addr = 0x{:x}", siginfo_addr); + + // 2. write ucontext_t. + user_rsp = alloc_aligned_in_user_stack(user_rsp, mem::size_of::(), 16)?; + let mut ucontext = ucontext_t::default(); + ucontext.uc_sigmask = mask.as_u64(); + ucontext.uc_mcontext.inner.gp_regs = context.gp_regs; + // TODO: store fp regs in ucontext + write_val_to_user(user_rsp as _, &ucontext)?; + let ucontext_addr = user_rsp; + debug!("ucontext addr = 0x{:x}", ucontext_addr); + // Store the ucontext addr in sig context of current process. + current.sig_context().lock().push_back(ucontext_addr as _); + + // 3. Set the address of the trampoline code. if flags.contains(SigActionFlags::SA_RESTORER) { // If contains SA_RESTORER flag, trampoline code is provided by libc in restorer_addr. // We just store restorer_addr on user stack to allow user code just to trampoline code. - user_rsp = write_u64_to_user_stack(user_rsp, restorer_addr as u64); + user_rsp = write_u64_to_user_stack(user_rsp, restorer_addr as u64)?; } else { // Otherwise we create const TRAMPOLINE: &[u8] = &[ @@ -128,44 +157,36 @@ pub fn handle_user_signal_handler( ]; user_rsp = user_rsp - TRAMPOLINE.len() as u64; let trampoline_rip = user_rsp; - write_bytes_to_user(user_rsp as Vaddr, TRAMPOLINE); - user_rsp = write_u64_to_user_stack(user_rsp, trampoline_rip); + write_bytes_to_user(user_rsp as Vaddr, TRAMPOLINE)?; + user_rsp = write_u64_to_user_stack(user_rsp, trampoline_rip)?; } + // 4. Set correct register values context.gp_regs.rip = handler_addr as _; context.gp_regs.rsp = user_rsp; // parameters of signal handler context.gp_regs.rdi = sig_num.as_u8() as u64; // signal number - context.gp_regs.rsi = 0; // siginfo_t* siginfo - context.gp_regs.rdx = 0; // void* ctx + if flags.contains(SigActionFlags::SA_SIGINFO) { + context.gp_regs.rsi = siginfo_addr; // siginfo_t* siginfo + context.gp_regs.rdx = ucontext_addr; // void* ctx + } else { + context.gp_regs.rsi = 0; + context.gp_regs.rdx = 0; + } + + Ok(()) } -fn write_u64_to_user_stack(rsp: u64, value: u64) -> u64 { +fn write_u64_to_user_stack(rsp: u64, value: u64) -> Result { let rsp = rsp - 8; - write_val_to_user(rsp as Vaddr, &value); - rsp + write_val_to_user(rsp as Vaddr, &value)?; + Ok(rsp) } -/// Used to store process context before running signal handler. -/// In rt_sigreturn, this context is used to restore process context. -#[derive(Debug, Clone, Copy)] -pub struct SigContext { - cpu_context: CpuContext, - sig_mask: SigMask, -} - -impl SigContext { - pub const fn new(cpu_context: CpuContext, sig_mask: SigMask) -> SigContext { - Self { - cpu_context, - sig_mask, - } - } - - pub fn cpu_context(&self) -> &CpuContext { - &self.cpu_context - } - - pub fn sig_mask(&self) -> &SigMask { - &self.sig_mask - } +/// alloc memory of size on user stack, the return address should respect the align argument. +fn alloc_aligned_in_user_stack(rsp: u64, size: usize, align: usize) -> Result { + if !align.is_power_of_two() { + return_errno_with_message!(Errno::EINVAL, "align must be power of two"); + } + let start = (rsp - size as u64).align_down(align as u64); + Ok(start) } diff --git a/src/kxos-std/src/process/signal/sig_action.rs b/src/kxos-std/src/process/signal/sig_action.rs index 3c8150463..5aabca9a9 100644 --- a/src/kxos-std/src/process/signal/sig_action.rs +++ b/src/kxos-std/src/process/signal/sig_action.rs @@ -104,6 +104,15 @@ impl SigActionFlags { pub fn to_u32(&self) -> u32 { self.bits() } + + pub fn contains_unsupported_flag(&self) -> bool { + self.intersects( + SigActionFlags::SA_NOCLDSTOP + | SigActionFlags::SA_NOCLDWAIT + | SigActionFlags::SA_ONSTACK + | SigActionFlags::SA_RESETHAND, + ) + } } /// The default action to signals diff --git a/src/kxos-std/src/process/signal/sig_mask.rs b/src/kxos-std/src/process/signal/sig_mask.rs index d80f971bc..f95eef642 100644 --- a/src/kxos-std/src/process/signal/sig_mask.rs +++ b/src/kxos-std/src/process/signal/sig_mask.rs @@ -11,6 +11,14 @@ impl From for SigMask { } } +impl From for SigMask { + fn from(sig_num: SigNum) -> Self { + let idx = SigMask::num_to_idx(sig_num); + let bits = 1u64 << idx; + SigMask { bits } + } +} + impl SigMask { pub fn new_empty() -> Self { SigMask::from(0u64) diff --git a/src/kxos-std/src/process/signal/signals/fault.rs b/src/kxos-std/src/process/signal/signals/fault.rs index 71477cb65..3bc336441 100644 --- a/src/kxos-std/src/process/signal/signals/fault.rs +++ b/src/kxos-std/src/process/signal/signals/fault.rs @@ -5,6 +5,7 @@ use kxos_frame::trap::{ }; use crate::prelude::*; +use crate::process::signal::c_types::siginfo_t; use crate::process::signal::constants::*; use crate::process::signal::sig_num::SigNum; @@ -48,4 +49,10 @@ impl Signal for FaultSignal { fn num(&self) -> SigNum { self.num } + + fn to_info(&self) -> siginfo_t { + siginfo_t::new(self.num, self.code) + // info.set_si_addr(self.addr.unwrap_or_default() as *const c_void); + // info + } } diff --git a/src/kxos-std/src/process/signal/signals/kernel.rs b/src/kxos-std/src/process/signal/signals/kernel.rs index c0cec7527..b91768293 100644 --- a/src/kxos-std/src/process/signal/signals/kernel.rs +++ b/src/kxos-std/src/process/signal/signals/kernel.rs @@ -1,4 +1,4 @@ -use crate::process::signal::sig_num::SigNum; +use crate::process::signal::{c_types::siginfo_t, constants::SI_KERNEL, sig_num::SigNum}; use super::Signal; @@ -17,4 +17,9 @@ impl Signal for KernelSignal { fn num(&self) -> SigNum { self.num } + + fn to_info(&self) -> siginfo_t { + let info = siginfo_t::new(self.num, SI_KERNEL); + info + } } diff --git a/src/kxos-std/src/process/signal/signals/mod.rs b/src/kxos-std/src/process/signal/signals/mod.rs index 71452ea2b..0d297c512 100644 --- a/src/kxos-std/src/process/signal/signals/mod.rs +++ b/src/kxos-std/src/process/signal/signals/mod.rs @@ -4,9 +4,11 @@ pub mod user; use core::fmt::Debug; -use super::sig_num::SigNum; +use super::{c_types::siginfo_t, sig_num::SigNum}; pub trait Signal: Send + Sync + Debug { /// Returns the number of the signal. fn num(&self) -> SigNum; + /// Returns the siginfo_t that gives more details about a signal. + fn to_info(&self) -> siginfo_t; } diff --git a/src/kxos-std/src/process/signal/signals/user.rs b/src/kxos-std/src/process/signal/signals/user.rs index cf0fa115f..98649ca24 100644 --- a/src/kxos-std/src/process/signal/signals/user.rs +++ b/src/kxos-std/src/process/signal/signals/user.rs @@ -1,4 +1,11 @@ -use crate::process::{signal::sig_num::SigNum, Pid}; +use crate::process::{ + signal::{ + c_types::siginfo_t, + constants::{SI_QUEUE, SI_TKILL, SI_USER}, + sig_num::SigNum, + }, + Pid, +}; use super::Signal; @@ -46,4 +53,21 @@ impl Signal for UserSignal { fn num(&self) -> SigNum { self.num } + + fn to_info(&self) -> siginfo_t { + let code = match self.kind { + UserSignalKind::Kill => SI_USER, + UserSignalKind::Tkill => SI_TKILL, + UserSignalKind::Sigqueue => SI_QUEUE, + }; + + let info = siginfo_t::new(self.num, code); + // info.set_si_pid(self.pid); + // info.set_si_uid(self.uid); + // if let UserSignalKind::Sigqueue(val) = self.kind { + // info.set_si_value(val); + // } + + info + } } diff --git a/src/kxos-std/src/process/task.rs b/src/kxos-std/src/process/task.rs index 229c312dd..d3f975cdf 100644 --- a/src/kxos-std/src/process/task.rs +++ b/src/kxos-std/src/process/task.rs @@ -57,7 +57,7 @@ pub fn create_new_task(userspace: Arc, parent: Weak) -> Arc< if current.status().lock().is_zombie() { break; } - handle_pending_signal(context); + handle_pending_signal(context).unwrap(); if current.status().lock().is_zombie() { debug!("exit due to signal"); break; @@ -66,10 +66,12 @@ pub fn create_new_task(userspace: Arc, parent: Weak) -> Arc< while current.status().lock().is_suspend() { Process::yield_now(); debug!("{} is suspended.", current.pid()); - handle_pending_signal(context); + handle_pending_signal(context).unwrap(); } } debug!("exit user loop"); + // Work around: exit in kernel task entry may be not called. Why this will happen? + Task::current().exit(); } Task::new(user_task_entry, parent, Some(userspace)).expect("spawn task failed") diff --git a/src/kxos-std/src/syscall/access.rs b/src/kxos-std/src/syscall/access.rs index 81319de7f..191943e5e 100644 --- a/src/kxos-std/src/syscall/access.rs +++ b/src/kxos-std/src/syscall/access.rs @@ -4,7 +4,7 @@ use crate::{memory::read_bytes_from_user, prelude::*, syscall::SYS_ACCESS}; pub fn sys_access(filename_ptr: Vaddr, file_mode: u64) -> Result { debug!("[syscall][id={}][SYS_ACCESS]", SYS_ACCESS); let mut filename_buffer = vec![0u8; MAX_FILENAME_LEN]; - read_bytes_from_user(filename_ptr, &mut filename_buffer); + read_bytes_from_user(filename_ptr, &mut filename_buffer)?; let filename = CString::from(CStr::from_bytes_until_nul(&filename_buffer).unwrap()); debug!("filename: {:?}", filename); warn!("access currenly does not check and just return success"); diff --git a/src/kxos-std/src/syscall/execve.rs b/src/kxos-std/src/syscall/execve.rs index af5592873..89ffe0238 100644 --- a/src/kxos-std/src/syscall/execve.rs +++ b/src/kxos-std/src/syscall/execve.rs @@ -12,7 +12,7 @@ pub fn sys_execve( ) -> Result { debug!("[syscall][id={}][SYS_EXECVE]", SYS_EXECVE); let mut filename_buffer = vec![0u8; MAX_FILENAME_LEN]; - read_bytes_from_user(filename_ptr, &mut filename_buffer); + read_bytes_from_user(filename_ptr, &mut filename_buffer)?; let filename = CString::from(CStr::from_bytes_until_nul(&filename_buffer).unwrap()); debug!("filename: {:?}", filename); diff --git a/src/kxos-std/src/syscall/futex.rs b/src/kxos-std/src/syscall/futex.rs index 6103533e4..b0ae280ef 100644 --- a/src/kxos-std/src/syscall/futex.rs +++ b/src/kxos-std/src/syscall/futex.rs @@ -333,7 +333,7 @@ impl FutexKey { pub fn load_val(&self) -> i32 { // FIXME: how to implement a atomic load? warn!("implement an atomic load"); - read_val_from_user(self.0) + read_val_from_user(self.0).unwrap() } pub fn addr(&self) -> Vaddr { diff --git a/src/kxos-std/src/syscall/readlink.rs b/src/kxos-std/src/syscall/readlink.rs index 39434cd13..4d530435a 100644 --- a/src/kxos-std/src/syscall/readlink.rs +++ b/src/kxos-std/src/syscall/readlink.rs @@ -20,20 +20,24 @@ pub fn sys_readlink( filename_ptr as Vaddr, user_buf_ptr as Vaddr, user_buf_len as usize, - ); + )?; Ok(SyscallReturn::Return(res as _)) } /// do sys readlink /// write the content to user buffer, returns the actual write len -pub fn do_sys_readlink(filename_ptr: Vaddr, user_buf_ptr: Vaddr, user_buf_len: usize) -> usize { +pub fn do_sys_readlink( + filename_ptr: Vaddr, + user_buf_ptr: Vaddr, + user_buf_len: usize, +) -> Result { debug!("filename ptr = 0x{:x}", filename_ptr); debug!("user_buf_ptr = 0x{:x}", user_buf_ptr); debug!("user_buf_len = 0x{:x}", user_buf_len); let mut filename_buffer = [0u8; MAX_FILENAME_LEN]; let current = Process::current(); - read_bytes_from_user(filename_ptr, &mut filename_buffer); + read_bytes_from_user(filename_ptr, &mut filename_buffer)?; let filename = CStr::from_bytes_until_nul(&filename_buffer).expect("Invalid filename"); debug!("filename = {:?}", filename); if filename == CString::new("/proc/self/exe").unwrap().as_c_str() { @@ -44,8 +48,8 @@ pub fn do_sys_readlink(filename_ptr: Vaddr, user_buf_ptr: Vaddr, user_buf_len: u let bytes_len = bytes.len(); let write_len = bytes_len.min(user_buf_len); - write_bytes_to_user(user_buf_ptr, &bytes[..write_len]); - return write_len; + write_bytes_to_user(user_buf_ptr, &bytes[..write_len])?; + return Ok(write_len); } panic!("does not support linkname other than /proc/self/exe") diff --git a/src/kxos-std/src/syscall/rt_sigaction.rs b/src/kxos-std/src/syscall/rt_sigaction.rs index 3205fa00f..bbd96e057 100644 --- a/src/kxos-std/src/syscall/rt_sigaction.rs +++ b/src/kxos-std/src/syscall/rt_sigaction.rs @@ -19,7 +19,7 @@ pub fn sys_rt_sigaction( debug!("sig_action_ptr = 0x{:x}", sig_action_ptr); debug!("old_sig_action_ptr = 0x{:x}", old_sig_action_ptr); debug!("sigset_size = {}", sigset_size); - let sig_action_c = read_val_from_user::(sig_action_ptr); + let sig_action_c = read_val_from_user::(sig_action_ptr)?; debug!("sig_action_c = {:?}", sig_action_c); let sig_action = SigAction::try_from(sig_action_c).unwrap(); debug!("sig_action = {:x?}", sig_action); @@ -31,6 +31,8 @@ pub fn sys_rt_sigaction( let old_action_c = old_action.to_c(); debug!("old_action_c = {:x?}", old_action_c); sig_dispositions.set(sig_num, sig_action); - write_val_to_user(old_sig_action_ptr, &old_action_c); + if old_sig_action_ptr != 0 { + write_val_to_user(old_sig_action_ptr, &old_action_c)?; + } Ok(SyscallReturn::Return(0)) } diff --git a/src/kxos-std/src/syscall/rt_sigreturn.rs b/src/kxos-std/src/syscall/rt_sigreturn.rs index 32df4110c..b816df719 100644 --- a/src/kxos-std/src/syscall/rt_sigreturn.rs +++ b/src/kxos-std/src/syscall/rt_sigreturn.rs @@ -1,14 +1,15 @@ -use crate::prelude::*; +use crate::{memory::read_val_from_user, prelude::*, process::signal::c_types::ucontext_t}; use kxos_frame::cpu::CpuContext; use super::SyscallReturn; pub fn sys_rt_sigreturn(context: &mut CpuContext) -> Result { let current = current!(); - let sig_context = current.sig_context().lock().as_ref().unwrap().clone(); - *context = *sig_context.cpu_context(); + let sig_context = current.sig_context().lock().pop_back().unwrap(); + let ucontext = read_val_from_user::(sig_context)?; + context.gp_regs = ucontext.uc_mcontext.inner.gp_regs; // unblock sig mask - let sig_mask = sig_context.sig_mask(); - current.sig_mask().lock().unblock(sig_mask.as_u64()); + let sig_mask = ucontext.uc_sigmask; + current.sig_mask().lock().unblock(sig_mask); Ok(SyscallReturn::NoReturn) } diff --git a/src/kxos-std/src/syscall/tgkill.rs b/src/kxos-std/src/syscall/tgkill.rs index 7312f7899..57a764c8f 100644 --- a/src/kxos-std/src/syscall/tgkill.rs +++ b/src/kxos-std/src/syscall/tgkill.rs @@ -1,13 +1,44 @@ use crate::prelude::*; +use crate::process::signal::sig_num::SigNum; +use crate::process::signal::signals::user::{UserSignal, UserSignalKind}; +use crate::process::{table, Pgid, Pid}; use crate::syscall::SYS_TGKILL; use super::SyscallReturn; -pub fn sys_tgkill(tgid: u64, pid: u64, signal: u64) -> Result { +/// tgkill send a signal to a thread with pid as its thread id, and tgid as its thread group id. +/// Since kxos only supports one-thread process now, tgkill will send signal to process with pid as its process id, +/// and tgid as its process group id. +pub fn sys_tgkill(tgid: Pgid, pid: Pid, sig_num: u8) -> Result { debug!("[syscall][id={}][SYS_TGKILL]", SYS_TGKILL); debug!("tgid = {}", tgid); debug!("pid = {}", pid); - warn!("TODO: tgkill do nothing now"); + let sig_num = SigNum::from_u8(sig_num); + debug!("sig_num = {:?}", sig_num); + let target_process = + table::pid_to_process(pid).ok_or(Error::with_message(Errno::EINVAL, "Invalid pid"))?; + let pgid = target_process.pgid(); + if pgid != tgid { + return_errno_with_message!( + Errno::EINVAL, + "the combination of tgid and pid is not valid" + ); + } + if target_process.status().lock().is_zombie() { + return Ok(SyscallReturn::Return(0)); + } + let signal = { + let src_pid = current!().pid(); + let src_uid = 0; + Box::new(UserSignal::new( + sig_num, + UserSignalKind::Tkill, + src_pid, + src_uid, + )) + }; + let mut sig_queue = target_process.sig_queues().lock(); + sig_queue.enqueue(signal); Ok(SyscallReturn::Return(0)) } diff --git a/src/kxos-std/src/syscall/uname.rs b/src/kxos-std/src/syscall/uname.rs index 5b32b8391..47dd9c081 100644 --- a/src/kxos-std/src/syscall/uname.rs +++ b/src/kxos-std/src/syscall/uname.rs @@ -60,15 +60,15 @@ fn copy_cstring_to_u8_slice(src: &CStr, dst: &mut [u8]) { pub fn sys_uname(old_uname_addr: u64) -> Result { debug!("[syscall][id={}][SYS_UNAME]", SYS_UNAME); - do_sys_uname(old_uname_addr as Vaddr); + do_sys_uname(old_uname_addr as Vaddr)?; Ok(SyscallReturn::Return(0)) } -pub fn do_sys_uname(old_uname_addr: Vaddr) -> usize { +pub fn do_sys_uname(old_uname_addr: Vaddr) -> Result { debug!("old_uname_addr: 0x{:x}", old_uname_addr); debug!("uts name size: {}", core::mem::size_of::()); debug!("uts name align: {}", core::mem::align_of::()); - write_val_to_user(old_uname_addr, &*UTS_NAME); - 0 + write_val_to_user(old_uname_addr, &*UTS_NAME)?; + Ok(0) } diff --git a/src/kxos-std/src/syscall/wait4.rs b/src/kxos-std/src/syscall/wait4.rs index 8e72e8e55..f0db5761b 100644 --- a/src/kxos-std/src/syscall/wait4.rs +++ b/src/kxos-std/src/syscall/wait4.rs @@ -18,7 +18,7 @@ pub fn sys_wait4(wait_pid: u64, exit_status_ptr: u64, wait_options: u64) -> Resu let process_filter = ProcessFilter::from_id(wait_pid as _); let (return_pid, exit_code) = wait_child_exit(process_filter, wait_options); if return_pid != 0 && exit_status_ptr != 0 { - write_val_to_user(exit_status_ptr as _, &exit_code); + write_val_to_user(exit_status_ptr as _, &exit_code)?; } Ok(SyscallReturn::Return(return_pid as _)) } diff --git a/src/kxos-std/src/syscall/write.rs b/src/kxos-std/src/syscall/write.rs index 291d582e8..2eddb05ea 100644 --- a/src/kxos-std/src/syscall/write.rs +++ b/src/kxos-std/src/syscall/write.rs @@ -13,12 +13,12 @@ pub fn sys_write(fd: u64, user_buf_ptr: u64, user_buf_len: u64) -> Result Result { debug!("[syscall][id={}][SYS_WRITEV]", SYS_WRITEV); - let res = do_sys_writev(fd, io_vec_ptr as Vaddr, io_vec_count as usize); + let res = do_sys_writev(fd, io_vec_ptr as Vaddr, io_vec_count as usize)?; Ok(SyscallReturn::Return(res as _)) } -pub fn do_sys_writev(fd: u64, io_vec_ptr: Vaddr, io_vec_count: usize) -> usize { +pub fn do_sys_writev(fd: u64, io_vec_ptr: Vaddr, io_vec_count: usize) -> Result { debug!("fd = {}", fd); debug!("io_vec_ptr = 0x{:x}", io_vec_ptr); debug!("io_vec_counter = 0x{:x}", io_vec_count); let mut write_len = 0; for i in 0..io_vec_count { - let io_vec = read_val_from_user::(io_vec_ptr + i * 8); + let io_vec = read_val_from_user::(io_vec_ptr + i * 8)?; let base = io_vec.base; let len = io_vec.len; debug!("base = 0x{:x}", base); debug!("len = {}", len); let mut buffer = vec![0u8; len]; - read_bytes_from_user(base, &mut buffer); + read_bytes_from_user(base, &mut buffer)?; let content = alloc::str::from_utf8(&buffer).unwrap(); write_len += len; if fd == 1 { @@ -46,5 +46,5 @@ pub fn do_sys_writev(fd: u64, io_vec_ptr: Vaddr, io_vec_count: usize) -> usize { panic!("Unsupported fd {}", fd); } } - write_len + Ok(write_len) } diff --git a/src/kxos-std/src/user_apps.rs b/src/kxos-std/src/user_apps.rs index 10ad61f9c..7daec2a53 100644 --- a/src/kxos-std/src/user_apps.rs +++ b/src/kxos-std/src/user_apps.rs @@ -27,38 +27,30 @@ pub fn get_all_apps() -> Vec { let mut res = Vec::with_capacity(16); // Most simple hello world, written in assembly - let app1 = UserApp::new("hello_world", read_hello_world_content()); - res.push(app1); + let asm_hello_world = UserApp::new("hello_world", read_hello_world_content()); + res.push(asm_hello_world); // Hello world, written in C language. // Since glibc requires the app name starts with "/", and we don't have filesystem now. // So we manually add a leading "/" for app written in C language. - let app2 = UserApp::new("/hello_c", read_hello_c_content()); - res.push(app2); + let hello_c = UserApp::new("/hello_c", read_hello_c_content()); + res.push(hello_c); // Fork process, written in assembly - let app3 = UserApp::new("fork", read_fork_content()); - res.push(app3); + let asm_fork = UserApp::new("fork", read_fork_content()); + res.push(asm_fork); // Execve, written in C language. - let app4 = UserApp::new("/execve", read_execve_content()); - res.push(app4); + let execve_c = UserApp::new("/execve", read_execve_content()); + res.push(execve_c); // Fork new process, written in C language. (Fork in glibc uses syscall clone actually) - let app5 = UserApp::new("/fork", read_fork_c_content()); - res.push(app5); + let fork_c = UserApp::new("/fork", read_fork_c_content()); + res.push(fork_c); - // Set sig procmask - let app6 = UserApp::new("/sig_procmask", read_sig_procmask_content()); - res.push(app6); - - // divide zero - let app7 = UserApp::new("/divide_zero", read_divide_zero_content()); - res.push(app7); - - // sig_action - let app8 = UserApp::new("/sig_action", read_sig_action_content()); - res.push(app8); + // signal test + let signal_test = UserApp::new("/signal_test", read_signal_test_content()); + res.push(signal_test); res } @@ -87,14 +79,6 @@ fn read_fork_c_content() -> &'static [u8] { include_bytes!("../../kxos-user/fork_c/fork") } -fn read_sig_procmask_content() -> &'static [u8] { - include_bytes!("../../kxos-user/signal_c/sig_procmask") -} - -fn read_divide_zero_content() -> &'static [u8] { - include_bytes!("../../kxos-user/signal_c/divide_zero") -} - -fn read_sig_action_content() -> &'static [u8] { - include_bytes!("../../kxos-user/signal_c/sig_action") +fn read_signal_test_content() -> &'static [u8] { + include_bytes!("../../kxos-user/signal_c/signal_test") } diff --git a/src/kxos-user/signal_c/Makefile b/src/kxos-user/signal_c/Makefile index 090aaaf3d..fd160a031 100644 --- a/src/kxos-user/signal_c/Makefile +++ b/src/kxos-user/signal_c/Makefile @@ -1,10 +1,7 @@ .PHONY: build clean run -build: divide_zero.c sig_procmask.c sig_action.c - @gcc -static divide_zero.c -o divide_zero - @gcc -static sig_procmask.c -o sig_procmask - @gcc -static sig_action.c -o sig_action +build: signal_test.c + @gcc -static signal_test.c -o signal_test clean: - @rm divide_zero sig_procmask sig_action + @rm signal_test run: build - @./sig_procmask - @./sig_action + @./signal_test diff --git a/src/kxos-user/signal_c/divide_zero b/src/kxos-user/signal_c/divide_zero deleted file mode 100755 index cad40895a..000000000 --- a/src/kxos-user/signal_c/divide_zero +++ /dev/null @@ -1,3 +0,0 @@ -version https://git-lfs.github.com/spec/v1 -oid sha256:07eb07c6387a9afd68a5b8a8bba9b48e1362b9c3b8f18ce30e744640a4dfa546 -size 871776 diff --git a/src/kxos-user/signal_c/divide_zero.c b/src/kxos-user/signal_c/divide_zero.c deleted file mode 100644 index 15be02cfe..000000000 --- a/src/kxos-user/signal_c/divide_zero.c +++ /dev/null @@ -1,7 +0,0 @@ -#include - -int main() { - int a = 2 - 2; - int b = 1 / a; - return 0; -} \ No newline at end of file diff --git a/src/kxos-user/signal_c/sig_action b/src/kxos-user/signal_c/sig_action deleted file mode 100755 index 348d203fc..000000000 --- a/src/kxos-user/signal_c/sig_action +++ /dev/null @@ -1,3 +0,0 @@ -version https://git-lfs.github.com/spec/v1 -oid sha256:1938c6e8abd70c400780dd8aa089d768a8662d54bac2a658936790d5537e868c -size 877544 diff --git a/src/kxos-user/signal_c/sig_action.c b/src/kxos-user/signal_c/sig_action.c deleted file mode 100644 index 7f33bb9e7..000000000 --- a/src/kxos-user/signal_c/sig_action.c +++ /dev/null @@ -1,29 +0,0 @@ -#include -#include -#include -#include -#include - -int sigchld = 0; - -void proc_exit() { - sigchld = sigchld + 1; -} - -int main() { - signal(SIGCHLD, proc_exit); - printf("Run a parent process has pid = %d\n", getpid()); - fflush(stdout); - int pid = fork(); - if(pid == 0) { - // child process - printf("create a new proces successfully (pid = %d)\n", getpid()); - fflush(stdout); - } else { - // parent process - wait(NULL); - printf("sigchld = %d\n", sigchld); - fflush(stdout); - } - return 0; -} \ No newline at end of file diff --git a/src/kxos-user/signal_c/sig_procmask b/src/kxos-user/signal_c/sig_procmask deleted file mode 100755 index 845c7aeb7..000000000 --- a/src/kxos-user/signal_c/sig_procmask +++ /dev/null @@ -1,3 +0,0 @@ -version https://git-lfs.github.com/spec/v1 -oid sha256:6faf986f058a2480297fb323a536319f9c7dde76efe4ed0472f09a4ad2960c9b -size 871848 diff --git a/src/kxos-user/signal_c/sig_procmask.c b/src/kxos-user/signal_c/sig_procmask.c deleted file mode 100644 index 3cd034bf0..000000000 --- a/src/kxos-user/signal_c/sig_procmask.c +++ /dev/null @@ -1,19 +0,0 @@ -/// This code is from CSAPP -/// We use this codes to test sigprocmask -#include -#include - -int main() { - sigset_t mask, prev_mask; - sigemptyset(&mask); - sigaddset(&mask, SIGINT); - sigaddset(&mask, SIGCHLD); - - /* Block SIGINT and save previous blocked set */ - sigprocmask(SIG_BLOCK, &mask, &prev_mask); - - // Code region that will not be interrupted by SIGINT and SIGCHILD - /* Restore previous blocked set, unblocking SIGINT */ - sigprocmask(SIG_SETMASK, &prev_mask, NULL); - return 0; -} \ No newline at end of file diff --git a/src/kxos-user/signal_c/signal_test b/src/kxos-user/signal_c/signal_test new file mode 100755 index 000000000..6c47eb9d5 --- /dev/null +++ b/src/kxos-user/signal_c/signal_test @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:25894b343f222eea5074a83e16a7140868a992a66e7bc8e363fabf2c23e19451 +size 882520 diff --git a/src/kxos-user/signal_c/signal_test.c b/src/kxos-user/signal_c/signal_test.c new file mode 100644 index 000000000..0d2e5bf5b --- /dev/null +++ b/src/kxos-user/signal_c/signal_test.c @@ -0,0 +1,285 @@ +// This test file is from occlum, to test whether we implement signal correctly. + +#define _GNU_SOURCE +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +// ============================================================================ +// Helper functions +// ============================================================================ + +#define THROW_ERROR(fmt, ...) do { \ + printf("\t\tERROR:" fmt " in func %s at line %d of file %s with errno %d: %s\n", \ + ##__VA_ARGS__, __func__, __LINE__, __FILE__, errno, strerror(errno)); \ + return -1; \ +} while (0) + + +// ============================================================================ +// Test sigprocmask +// ============================================================================ + +#define sigcmpset(a, b) memcmp((a), (b), 8) + +int test_sigprocmask() { + int ret; + sigset_t new, old; + sigset_t expected_old; + + // Check sigmask == [] + if ((ret = sigprocmask(0, NULL, &old)) < 0) { + THROW_ERROR("sigprocmask failed unexpectedly"); + } + sigemptyset(&expected_old); + if (sigcmpset(&old, &expected_old) != 0) { + THROW_ERROR("unexpected old sigset"); + } + + // SIG_BLOCK: [] --> [SIGSEGV] + sigemptyset(&new); + sigaddset(&new, SIGSEGV); + if ((ret = sigprocmask(SIG_BLOCK, &new, &old)) < 0) { + THROW_ERROR("sigprocmask failed unexpectedly"); + } + sigemptyset(&expected_old); + if (sigcmpset(&old, &expected_old) != 0) { + THROW_ERROR("unexpected old sigset"); + } + + // SIG_SETMASK: [SIGSEGV] --> [SIGIO] + sigemptyset(&new); + sigaddset(&new, SIGIO); + if ((ret = sigprocmask(SIG_SETMASK, &new, &old)) < 0) { + THROW_ERROR("sigprocmask failed unexpectedly"); + } + sigemptyset(&expected_old); + sigaddset(&expected_old, SIGSEGV); + if (sigcmpset(&old, &expected_old) != 0) { + THROW_ERROR("unexpected old sigset"); + } + + // SIG_UNBLOCK: [SIGIO] -> [] + if ((ret = sigprocmask(SIG_UNBLOCK, &new, &old)) < 0) { + THROW_ERROR("sigprocmask failed unexpectedly"); + } + sigemptyset(&expected_old); + sigaddset(&expected_old, SIGIO); + if (sigcmpset(&old, &expected_old) != 0) { + THROW_ERROR("unexpected old sigset"); + } + + // Check sigmask == [] + if ((ret = sigprocmask(0, NULL, &old)) < 0) { + THROW_ERROR("sigprocmask failed unexpectedly"); + } + sigemptyset(&expected_old); + if (sigcmpset(&old, &expected_old) != 0) { + THROW_ERROR("unexpected old sigset"); + } + + return 0; +} + +// ============================================================================ +// Test raise syscall and user-registered signal handlers +// ============================================================================ + +#define MAX_RECURSION_LEVEL 3 + +static void handle_sigio(int num, siginfo_t *info, void *context) { + static volatile int recursion_level = 0; + printf("Hello from SIGIO signal handler (recursion_level = %d)!\n", recursion_level); + fflush(stdout); + + recursion_level++; + if (recursion_level <= MAX_RECURSION_LEVEL) { + raise(SIGIO); + } + recursion_level--; +} + +int test_raise() { + struct sigaction new_action, old_action; + memset(&new_action, 0, sizeof(struct sigaction)); + memset(&old_action, 0, sizeof(struct sigaction)); + new_action.sa_sigaction = handle_sigio; + new_action.sa_flags = SA_SIGINFO | SA_NODEFER; + if (sigaction(SIGIO, &new_action, &old_action) < 0) { + THROW_ERROR("registering new signal handler failed"); + } + if (old_action.sa_handler != SIG_DFL) { + THROW_ERROR("unexpected old sig handler"); + } + + raise(SIGIO); + + if (sigaction(SIGIO, &old_action, NULL) < 0) { + THROW_ERROR("restoring old signal handler failed"); + } + return 0; +} + +// ============================================================================ +// Test catching and handling hardware exception +// ============================================================================ + +static void handle_sigfpe(int num, siginfo_t *info, void *_context) { + printf("SIGFPE Caught\n"); + fflush(stdout); + assert(num == SIGFPE); + assert(info->si_signo == SIGFPE); + ucontext_t *ucontext = _context; + mcontext_t *mcontext = &ucontext->uc_mcontext; + // The faulty instruction should be `idiv %esi` (f7 fe) + mcontext->gregs[REG_RIP] += 2; + return; +} + +// Note: this function is fragile in the sense that compiler may not always +// emit the instruction pattern that triggers divide-by-zero as we expect. +// TODO: rewrite this in assembly +int div_maybe_zero(int x, int y) { + return x / y; +} + +#define fxsave(addr) __asm __volatile("fxsave %0" : "=m" (*(addr))) + +int test_handle_sigfpe() { + // Set up a signal handler that handles divide-by-zero exception + struct sigaction new_action, old_action; + memset(&new_action, 0, sizeof(struct sigaction)); + memset(&old_action, 0, sizeof(struct sigaction)); + new_action.sa_sigaction = handle_sigfpe; + new_action.sa_flags = SA_SIGINFO; + if (sigaction(SIGFPE, &new_action, &old_action) < 0) { + THROW_ERROR("registering new signal handler failed"); + } + if (old_action.sa_handler != SIG_DFL) { + THROW_ERROR("unexpected old sig handler"); + } + + char x[512] __attribute__((aligned(16))) = {}; + char y[512] __attribute__((aligned(16))) = {}; + + // Trigger divide-by-zero exception + int a = 1; + int b = 0; + // Use volatile to prevent compiler optimization + volatile int c; + fxsave(x); + c = div_maybe_zero(a, b); + fxsave(y); + + // kxos does not save and restore fpregs now, so we emit this check. + // if (memcmp(x, y, 512) != 0) { + // THROW_ERROR("floating point registers are modified"); + // } + + printf("Signal handler successfully jumped over the divide-by-zero instruction\n"); + fflush(stdout); + + if (sigaction(SIGFPE, &old_action, NULL) < 0) { + THROW_ERROR("restoring old signal handler failed"); + } + return 0; +} + +// TODO: rewrite this in assembly +int read_maybe_null(int *p) { + return *p; +} + +static void handle_sigsegv(int num, siginfo_t *info, void *_context) { + printf("SIGSEGV Caught\n"); + fflush(stdout); + + assert(num == SIGSEGV); + assert(info->si_signo == SIGSEGV); + + ucontext_t *ucontext = _context; + mcontext_t *mcontext = &ucontext->uc_mcontext; + // TODO: how long is the instruction? + // The faulty instruction should be `idiv %esi` (f7 fe) + mcontext->gregs[REG_RIP] += 2; + + return; +} + + +int test_handle_sigsegv() { + // Set up a signal handler that handles divide-by-zero exception + struct sigaction new_action, old_action; + memset(&new_action, 0, sizeof(struct sigaction)); + memset(&old_action, 0, sizeof(struct sigaction)); + new_action.sa_sigaction = handle_sigsegv; + new_action.sa_flags = SA_SIGINFO; + if (sigaction(SIGSEGV, &new_action, &old_action) < 0) { + THROW_ERROR("registering new signal handler failed"); + } + if (old_action.sa_handler != SIG_DFL) { + THROW_ERROR("unexpected old sig handler"); + } + + int *addr = NULL; + volatile int val = read_maybe_null(addr); + (void)val; // to suppress "unused variables" warning + + printf("Signal handler successfully jumped over a null-dereferencing instruction\n"); + fflush(stdout); + + if (sigaction(SIGSEGV, &old_action, NULL) < 0) { + THROW_ERROR("restoring old signal handler failed"); + } + return 0; +} + +// ============================================================================ +// Test SIGCHLD signal +// ============================================================================ +int sigchld = 0; + +void proc_exit() { + sigchld = 1; +} + +int test_sigchld() { + signal(SIGCHLD, proc_exit); + printf("Run a parent process has pid = %d\n", getpid()); + fflush(stdout); + int pid = fork(); + if(pid == 0) { + // child process + printf("create a new proces successfully (pid = %d)\n", getpid()); + fflush(stdout); + } else { + // parent process + wait(NULL); + printf("sigchld = %d\n", sigchld); + fflush(stdout); + } + return 0; +} + +int main() { + test_sigprocmask(); + test_raise(); + test_handle_sigfpe(); + test_handle_sigsegv(); + test_sigchld(); + return 0; +} \ No newline at end of file diff --git a/src/kxos-virtio/Cargo.toml b/src/kxos-virtio/Cargo.toml index f08353095..e248d9693 100644 --- a/src/kxos-virtio/Cargo.toml +++ b/src/kxos-virtio/Cargo.toml @@ -11,7 +11,8 @@ spin = "0.9.4" kxos-frame = {path = "../kxos-frame"} kxos-pci = {path="../kxos-pci"} kxos-util = {path="../kxos-util"} -kxos-frame-pod-derive= {path="../kxos-frame-pod-derive"} +kxos-frame-pod-derive = {path = "../kxos-frame-pod-derive"} + [features] diff --git a/src/kxos-virtio/src/block.rs b/src/kxos-virtio/src/block.rs index 60f6e3ada..56a0f083e 100644 --- a/src/kxos-virtio/src/block.rs +++ b/src/kxos-virtio/src/block.rs @@ -1,5 +1,4 @@ use kxos_frame::Pod; -use kxos_frame_pod_derive::Pod; use kxos_pci::capability::vendor::virtio::CapabilityVirtioData; use kxos_pci::util::BAR; use kxos_util::frame_ptr::InFramePtr; diff --git a/src/kxos-virtio/src/lib.rs b/src/kxos-virtio/src/lib.rs index d226a4ee5..88bc3534c 100644 --- a/src/kxos-virtio/src/lib.rs +++ b/src/kxos-virtio/src/lib.rs @@ -14,9 +14,9 @@ use kxos_util::frame_ptr::InFramePtr; use spin::{mutex::Mutex, MutexGuard}; use self::{block::VirtioBLKConfig, queue::VirtQueue}; - -use kxos_frame::Pod; use kxos_pci::{capability::vendor::virtio::CapabilityVirtioData, msix::MSIX}; +#[macro_use] +extern crate kxos_frame_pod_derive; pub mod block; pub mod queue; diff --git a/src/kxos-virtio/src/queue.rs b/src/kxos-virtio/src/queue.rs index 92f357ed1..218794333 100644 --- a/src/kxos-virtio/src/queue.rs +++ b/src/kxos-virtio/src/queue.rs @@ -6,7 +6,6 @@ use bitflags::bitflags; use core::sync::atomic::{fence, Ordering}; use kxos_frame::offset_of; use kxos_frame::Pod; -use kxos_frame_pod_derive::Pod; use kxos_util::frame_ptr::InFramePtr; #[derive(Debug)] pub enum QueueError {