Implement signal delivery code (untested).
This commit is contained in:
@@ -110,6 +110,12 @@ impl InterruptStack {
|
||||
pub fn stack_pointer(&self) -> usize {
|
||||
self.iret.rsp
|
||||
}
|
||||
pub fn instr_pointer(&self) -> usize {
|
||||
self.iret.rip
|
||||
}
|
||||
pub fn flags(&self) -> usize {
|
||||
self.iret.rflags
|
||||
}
|
||||
pub fn set_instr_pointer(&mut self, rip: usize) {
|
||||
self.iret.rip = rip;
|
||||
}
|
||||
@@ -193,6 +199,14 @@ impl InterruptStack {
|
||||
self.iret.rflags & FLAG_SINGLESTEP == FLAG_SINGLESTEP
|
||||
}
|
||||
}
|
||||
impl ScratchRegisters {
|
||||
pub fn a(&self) -> usize {
|
||||
self.rax
|
||||
}
|
||||
pub fn b(&self) -> usize {
|
||||
self.rdx
|
||||
}
|
||||
}
|
||||
|
||||
#[macro_export]
|
||||
macro_rules! push_scratch {
|
||||
|
||||
@@ -452,8 +452,9 @@ impl Context {
|
||||
Some(unsafe { &mut *kstack.initial_top().sub(size_of::<InterruptStack>()).cast() })
|
||||
}
|
||||
pub fn sigcontrol(&mut self) -> Option<(&Sigcontrol, &SigProcControl, &mut SignalState)> {
|
||||
let sig = self.sig.as_mut()?;
|
||||
|
||||
Some(Self::sigcontrol_raw(self.sig.as_mut()?))
|
||||
}
|
||||
pub fn sigcontrol_raw(sig: &mut SignalState) -> (&Sigcontrol, &SigProcControl, &mut SignalState) {
|
||||
let check = |off| {
|
||||
assert_eq!(usize::from(off) % mem::align_of::<usize>(), 0);
|
||||
assert!(usize::from(off).saturating_add(mem::size_of::<Sigcontrol>()) < PAGE_SIZE);
|
||||
@@ -470,7 +471,7 @@ impl Context {
|
||||
.data() as *const SigProcControl).byte_add(usize::from(sig.procctl_off))
|
||||
};
|
||||
|
||||
Some((for_thread, for_proc, sig))
|
||||
(for_thread, for_proc, sig)
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
+52
-9
@@ -2,10 +2,9 @@ use alloc::sync::Arc;
|
||||
use core::mem::size_of;
|
||||
use syscall::{
|
||||
flag::{
|
||||
PTRACE_FLAG_IGNORE, PTRACE_STOP_SIGNAL, SIGCHLD, SIGCONT, SIGKILL, SIGSTOP, SIGTSTP,
|
||||
SIGTTIN, SIGTTOU, SIGTERM,
|
||||
PTRACE_FLAG_IGNORE, PTRACE_STOP_SIGNAL, SIGCHLD, SIGCONT, SIGKILL, SIGSTOP, SIGTERM, SIGTSTP, SIGTTIN, SIGTTOU
|
||||
},
|
||||
ptrace_event, IntRegisters,
|
||||
ptrace_event, IntRegisters, SigcontrolFlags,
|
||||
};
|
||||
|
||||
use crate::{
|
||||
@@ -41,16 +40,60 @@ pub fn kmain_signal_handler() {
|
||||
}
|
||||
|
||||
pub fn signal_handler() {
|
||||
/*let thumbs_down = ptrace::breakpoint_callback(
|
||||
let context_lock = context::current().expect("running signal handler outside of context");
|
||||
let mut context = context_lock.write();
|
||||
let context = &mut *context;
|
||||
|
||||
if context.being_sigkilled {
|
||||
crate::syscall::process::exit(SIGKILL << 8);
|
||||
}
|
||||
|
||||
let thumbs_down = ptrace::breakpoint_callback(
|
||||
PTRACE_STOP_SIGNAL,
|
||||
Some(ptrace_event!(PTRACE_STOP_SIGNAL, sig, handler)),
|
||||
Some(ptrace_event!(PTRACE_STOP_SIGNAL)),
|
||||
)
|
||||
.and_then(|_| ptrace::next_breakpoint().map(|f| f.contains(PTRACE_FLAG_IGNORE)));*/
|
||||
.and_then(|_| ptrace::next_breakpoint().map(|f| f.contains(PTRACE_FLAG_IGNORE)));
|
||||
|
||||
// TODO: thumbs_down
|
||||
let Some((thread_ctl, proc_ctl, st)) = context.sigcontrol() else {
|
||||
// Discard signal if sigcontrol is unset.
|
||||
return;
|
||||
};
|
||||
if unsafe { thread_ctl.control_flags.get().read() }.contains(SigcontrolFlags::INHIBIT_DELIVERY) {
|
||||
// Signals are inhibited to protect critical sections inside libc, but this code will run
|
||||
// every time the context is switched to.
|
||||
return;
|
||||
}
|
||||
|
||||
if !core::mem::take(&mut st.is_pending) {
|
||||
return;
|
||||
}
|
||||
|
||||
let Some(regs) = context.regs_mut() else {
|
||||
// TODO: is this even reachable?
|
||||
return;
|
||||
};
|
||||
|
||||
let ip = regs.instr_pointer();
|
||||
let sp = regs.stack_pointer();
|
||||
let fl = regs.flags();
|
||||
let scratch_a = regs.scratch.a();
|
||||
let scratch_b = regs.scratch.b();
|
||||
|
||||
let (thread_ctl, _, _) = context.sigcontrol()
|
||||
.expect("cannot have been unset while holding the lock");
|
||||
|
||||
unsafe {
|
||||
thread_ctl.saved_ip.get().write_volatile(ip);
|
||||
thread_ctl.saved_sp.get().write_volatile(sp);
|
||||
thread_ctl.saved_flags.get().write_volatile(fl);
|
||||
thread_ctl.saved_scratch_a.get().write_volatile(scratch_a);
|
||||
thread_ctl.saved_scratch_b.get().write_volatile(scratch_b);
|
||||
(*thread_ctl.control_flags.get()) |= SigcontrolFlags::INHIBIT_DELIVERY;
|
||||
}
|
||||
}
|
||||
pub fn excp_handler(signal: usize) {
|
||||
let Ok(current) = context::current() else {
|
||||
panic!("CPU exception but not inside of context! Trying to switch...");
|
||||
};
|
||||
let current = context::current().expect("CPU exception but not inside of context!");
|
||||
let mut context = current.write();
|
||||
|
||||
let Some(eh) = context.sig.as_ref().and_then(|s| s.excp_handler) else {
|
||||
|
||||
+14
-3
@@ -162,11 +162,18 @@ pub fn kill(pid: ContextId, sig: usize) -> Result<usize> {
|
||||
return true;
|
||||
}
|
||||
|
||||
// Convert stopped processes to blocked if sending SIGCONT
|
||||
if sig == SIGCONT && let context::Status::Stopped(_sig) = context.status {
|
||||
// Convert stopped processes to blocked if sending SIGCONT, regardless of whether
|
||||
// SIGCONT is blocked or ignored. It can however be controlled whether the process
|
||||
// will additionally ignore, defer, or handle that signal.
|
||||
context.status = context::Status::Blocked;
|
||||
if let Some((ctl, _, _)) = context.sigcontrol() {
|
||||
|
||||
if let Some((ctl, _, st)) = context.sigcontrol() {
|
||||
ctl.word[0].fetch_and(!(sig_bit(SIGTTIN) | sig_bit(SIGTTOU) | sig_bit(SIGTSTP)), Ordering::Relaxed);
|
||||
ctl.word[0].fetch_or(sig_bit(SIGCONT), Ordering::Relaxed);
|
||||
if (ctl.word[0].load(Ordering::Relaxed) >> 32) & sig_bit(SIGCONT) == 0 {
|
||||
st.is_pending = true;
|
||||
}
|
||||
}
|
||||
} else if sig == SIGSTOP || (matches!(sig, SIGTTIN | SIGTTOU | SIGTSTP) && context.sigcontrol().map_or(false, |(_, proc, _)| proc.signal_will_stop(sig))) {
|
||||
context.status = context::Status::Stopped(sig);
|
||||
@@ -181,7 +188,11 @@ pub fn kill(pid: ContextId, sig: usize) -> Result<usize> {
|
||||
st.is_pending = true;
|
||||
}
|
||||
} else {
|
||||
context.being_sigkilled = true;
|
||||
// Discard signals if sighandler is unset. This includes both special contexts such
|
||||
// as bootstrap, and child processes or threads that have not yet been started.
|
||||
// This is semantically equivalent to having all signals except SIGSTOP and SIGKILL
|
||||
// blocked/ignored (SIGCONT can be ignored and masked, but will always continue
|
||||
// stopped processes first).
|
||||
}
|
||||
|
||||
true
|
||||
|
||||
Reference in New Issue
Block a user