extern crate alloc;
use alloc::sync::Arc;
use axerrno::{AxError, AxResult};
use axhal::{
arch::{read_trapframe_from_kstack, write_trapframe_to_kstack, TrapFrame},
cpu::this_cpu_id,
KERNEL_PROCESS_ID,
};
use axlog::{info, warn};
use axsignal::{
action::{SigActionFlags, SignalDefault, SIG_IGN},
info::SigInfo,
signal_no::SignalNo,
ucontext::SignalUserContext,
SignalHandler, SignalSet,
};
use axsync::Mutex;
use axtask::{TaskState, RUN_QUEUE};
pub struct SignalModule {
pub sig_info: bool,
pub last_trap_frame_for_signal: Option<TrapFrame>,
pub signal_handler: Arc<Mutex<SignalHandler>>,
pub signal_set: SignalSet,
}
impl SignalModule {
pub fn init_signal(signal_handler: Option<Arc<Mutex<SignalHandler>>>) -> Self {
let signal_handler =
signal_handler.unwrap_or_else(|| Arc::new(Mutex::new(SignalHandler::new())));
let signal_set = SignalSet::new();
let last_trap_frame_for_signal = None;
let sig_info = false;
Self {
sig_info,
last_trap_frame_for_signal,
signal_handler,
signal_set,
}
}
}
const USER_SIGNAL_PROTECT: usize = 512;
use crate::{
current_process, current_task, exit_current_task,
process::{PID2PC, TID2TASK},
};
#[no_mangle]
pub fn load_trap_for_signal() -> bool {
let current_process = current_process();
let current_task = current_task();
let mut signal_modules = current_process.signal_modules.lock();
let signal_module = signal_modules.get_mut(¤t_task.id().as_u64()).unwrap();
if let Some(old_trap_frame) = signal_module.last_trap_frame_for_signal.take() {
unsafe {
let mut now_trap_frame =
read_trapframe_from_kstack(current_task.get_kernel_stack_top().unwrap());
let sp = now_trap_frame.get_sp();
now_trap_frame = old_trap_frame;
if signal_module.sig_info {
let pc = (*(sp as *const SignalUserContext)).get_pc();
now_trap_frame.set_pc(pc);
}
write_trapframe_to_kstack(
current_task.get_kernel_stack_top().unwrap(),
&now_trap_frame,
);
}
true
} else {
false
}
}
fn terminate_process(signal: SignalNo) {
let current_task = current_task();
warn!("Terminate process: {}", current_task.get_process_id());
if current_task.is_leader() {
exit_current_task(signal as i32);
} else {
send_signal_to_process(current_task.get_process_id() as isize, signal as isize).unwrap();
exit_current_task(-1);
}
}
pub fn handle_signals() {
let process = current_process();
let current_task = current_task();
if let Some(signal_no) = current_task.check_pending_signal() {
send_signal_to_thread(current_task.id().as_u64() as isize, signal_no as isize)
.unwrap_or_else(|err| {
warn!("send signal failed: {:?}", err);
});
}
if process.get_zombie() {
if current_task.is_leader() {
return;
}
exit_current_task(0);
}
if process.pid() == KERNEL_PROCESS_ID {
return;
}
let mut signal_modules = process.signal_modules.lock();
let signal_module = signal_modules.get_mut(¤t_task.id().as_u64()).unwrap();
let signal_set = &mut signal_module.signal_set;
let sig_num = if let Some(sig_num) = signal_set.get_one_signal() {
sig_num
} else {
return;
};
info!(
"cpu: {}, task: {}, handler signal: {}",
this_cpu_id(),
current_task.id().as_u64(),
sig_num
);
let signal = SignalNo::from(sig_num);
let mask = signal_set.mask;
if signal_module.last_trap_frame_for_signal.is_some() {
if signal == SignalNo::SIGSEGV || signal == SignalNo::SIGBUS {
drop(signal_modules);
exit_current_task(-1);
}
return;
}
signal_module.last_trap_frame_for_signal = Some(read_trapframe_from_kstack(
current_task.get_kernel_stack_top().unwrap(),
));
signal_module.sig_info = false;
let signal_handler = signal_module.signal_handler.lock();
let action = signal_handler.get_action(sig_num);
if action.is_none() {
drop(signal_handler);
drop(signal_modules);
match SignalDefault::get_action(signal) {
SignalDefault::Ignore => {
load_trap_for_signal();
}
SignalDefault::Terminate => {
terminate_process(signal);
}
SignalDefault::Stop => {
unimplemented!();
}
SignalDefault::Cont => {
unimplemented!();
}
SignalDefault::Core => {
terminate_process(signal);
}
}
return;
}
let action = action.unwrap();
if action.sa_handler == SIG_IGN {
return;
}
let mut trap_frame = read_trapframe_from_kstack(current_task.get_kernel_stack_top().unwrap());
let mut sp = trap_frame.get_sp() - USER_SIGNAL_PROTECT;
let restorer = if let Some(addr) = action.get_storer() {
addr
} else {
axconfig::SIGNAL_TRAMPOLINE
};
info!(
"restorer :{:#x}, handler: {:#x}",
restorer, action.sa_handler
);
#[cfg(not(target_arch = "x86_64"))]
trap_frame.set_ra(restorer);
let old_pc = trap_frame.get_pc();
trap_frame.set_pc(action.sa_handler);
trap_frame.set_arg0(sig_num);
if action.sa_flags.contains(SigActionFlags::SA_SIGINFO) {
signal_module.sig_info = true;
let sp_base = (((sp - core::mem::size_of::<SigInfo>()) & !0xf)
- core::mem::size_of::<SignalUserContext>())
& !0xf;
process
.manual_alloc_range_for_lazy(sp_base.into(), sp.into())
.expect("Failed to alloc memory for signal user stack");
sp = (sp - core::mem::size_of::<SigInfo>()) & !0xf;
let info = SigInfo {
si_signo: sig_num as i32,
..Default::default()
};
unsafe {
*(sp as *mut SigInfo) = info;
}
trap_frame.set_arg1(sp);
sp = (sp - core::mem::size_of::<SignalUserContext>()) & !0xf;
let ucontext = SignalUserContext::init(old_pc, mask);
unsafe {
*(sp as *mut SignalUserContext) = ucontext;
}
trap_frame.set_arg2(sp);
}
#[cfg(target_arch = "x86_64")]
unsafe {
sp -= core::mem::size_of::<usize>();
*(sp as *mut usize) = restorer;
}
trap_frame.set_user_sp(sp);
write_trapframe_to_kstack(current_task.get_kernel_stack_top().unwrap(), &trap_frame);
drop(signal_handler);
drop(signal_modules);
}
pub fn signal_return() -> isize {
if load_trap_for_signal() {
read_trapframe_from_kstack(current_task().get_kernel_stack_top().unwrap()).get_ret_code()
as isize
} else {
-1
}
}
pub fn send_signal_to_process(pid: isize, signum: isize) -> AxResult<()> {
let mut pid2pc = PID2PC.lock();
if !pid2pc.contains_key(&(pid as u64)) {
return Err(axerrno::AxError::NotFound);
}
let process = pid2pc.get_mut(&(pid as u64)).unwrap();
let mut now_id: Option<u64> = None;
for task in process.tasks.lock().iter_mut() {
if task.is_leader() {
now_id = Some(task.id().as_u64());
break;
}
}
if now_id.is_some() {
let mut signal_modules = process.signal_modules.lock();
let signal_module = signal_modules.get_mut(&now_id.unwrap()).unwrap();
signal_module.signal_set.try_add_signal(signum as usize);
let tid2task = TID2TASK.lock();
let main_task = Arc::clone(tid2task.get(&now_id.unwrap()).unwrap());
if main_task.state() == TaskState::Blocked {
RUN_QUEUE.lock().unblock_task(main_task, false);
}
}
Ok(())
}
pub fn send_signal_to_thread(tid: isize, signum: isize) -> AxResult<()> {
let tid2task = TID2TASK.lock();
let task = if let Some(task) = tid2task.get(&(tid as u64)) {
Arc::clone(task)
} else {
return Err(AxError::NotFound);
};
drop(tid2task);
let pid = task.get_process_id();
let pid2pc = PID2PC.lock();
let process = if let Some(process) = pid2pc.get(&pid) {
Arc::clone(process)
} else {
return Err(AxError::NotFound);
};
drop(pid2pc);
let mut signal_modules = process.signal_modules.lock();
if !signal_modules.contains_key(&(tid as u64)) {
return Err(axerrno::AxError::NotFound);
}
let signal_module = signal_modules.get_mut(&(tid as u64)).unwrap();
signal_module.signal_set.try_add_signal(signum as usize);
if task.state() == TaskState::Blocked {
RUN_QUEUE.lock().unblock_task(task, false);
}
Ok(())
}