573b3e6eae
excp_handler() called context::current() unconditionally, which panics with 'not inside of context' when no context exists yet (before context::init() runs in kmain/kmain_ap). On bare metal, a page fault during BSP's start() — e.g. ACPI table access or device MMIO — caused page_fault_handler() to return Err, falling through to excp_handler(), which then panicked at context::current() instead of reporting the actual fault. Replace context::current() with context::try_current(). When None, log the exception details (kind, code, faulting address) and panic with a descriptive message. This turns an uninformative cascading panic into a diagnostic one that reveals the real faulting address.
120 lines
4.2 KiB
Rust
120 lines
4.2 KiB
Rust
use core::sync::atomic::Ordering;
|
|
|
|
use crate::{context, sync::CleanLockToken, syscall::flag::SigcontrolFlags};
|
|
|
|
pub fn signal_handler(token: &mut CleanLockToken) {
|
|
let context_lock = context::current();
|
|
let mut context_guard = context_lock.write(token.token());
|
|
let context = &mut *context_guard;
|
|
|
|
let being_sigkilled = context.being_sigkilled;
|
|
|
|
if being_sigkilled {
|
|
drop(context_guard);
|
|
drop(context_lock);
|
|
crate::syscall::process::exit_this_context(None, token);
|
|
}
|
|
|
|
/*let thumbs_down = ptrace::breakpoint_callback(
|
|
PTRACE_STOP_SIGNAL,
|
|
Some(ptrace_event!(PTRACE_STOP_SIGNAL)),
|
|
)
|
|
.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.
|
|
trace!("no sigcontrol, returning");
|
|
return;
|
|
};
|
|
if thread_ctl.currently_pending_unblocked(proc_ctl) == 0 {
|
|
// The context is currently Runnable. When transitioning into Blocked, it will check for
|
|
// signals (with the context lock held, which is required when sending signals). After
|
|
// that, any detection of pending unblocked signals by the sender, will result in the
|
|
// context being unblocked, and signals sent.
|
|
|
|
// TODO: prioritize signals over regular program execution
|
|
return;
|
|
}
|
|
let control_flags =
|
|
SigcontrolFlags::from_bits_retain(thread_ctl.control_flags.load(Ordering::Acquire));
|
|
|
|
if control_flags.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.
|
|
trace!("Inhibiting delivery, returning");
|
|
return;
|
|
}
|
|
|
|
let sigh_instr_ptr = st.user_handler.get();
|
|
|
|
let Some(regs) = context.regs_mut() else {
|
|
// TODO: is this even reachable?
|
|
trace!("No registers, returning");
|
|
return;
|
|
};
|
|
|
|
let ip = regs.instr_pointer();
|
|
let archdep_reg = regs.sig_archdep_reg();
|
|
|
|
regs.set_instr_pointer(sigh_instr_ptr);
|
|
|
|
let (thread_ctl, _, _) = context
|
|
.sigcontrol()
|
|
.expect("cannot have been unset while holding the lock");
|
|
|
|
thread_ctl.saved_ip.set(ip);
|
|
thread_ctl.saved_archdep_reg.set(archdep_reg);
|
|
|
|
thread_ctl.control_flags.store(
|
|
(control_flags | SigcontrolFlags::INHIBIT_DELIVERY).bits(),
|
|
Ordering::Release,
|
|
);
|
|
}
|
|
pub fn excp_handler(excp: syscall::Exception) {
|
|
let mut token = unsafe { CleanLockToken::new() };
|
|
|
|
let Some(current) = context::try_current() else {
|
|
// No context exists — this happens during early boot (before
|
|
// context::init() in kmain) or very early in AP startup. The
|
|
// exception details (page fault address, stack trace, etc.) have
|
|
// already been printed by the caller in the exception handler.
|
|
// There is no userspace context to deliver a signal to, so halt.
|
|
info!(
|
|
"excp_handler: no current context (early boot), CPU {}, kind {}, code {}, address {:#x}",
|
|
crate::cpu_id(),
|
|
excp.kind,
|
|
excp.code,
|
|
excp.address
|
|
);
|
|
panic!("unhandled exception during early boot (no context)");
|
|
};
|
|
|
|
let context = current.write(token.token());
|
|
|
|
let Some(eh) = context.sig.as_ref().and_then(|s| s.excp_handler) else {
|
|
// TODO: Let procmgr print this?
|
|
info!(
|
|
"UNHANDLED EXCEPTION, CPU {}, PID {}, NAME {}, CONTEXT {current:p}",
|
|
crate::cpu_id(),
|
|
context.pid,
|
|
context.name
|
|
);
|
|
drop(context);
|
|
// TODO: Allow exceptions to be caught by tracer etc, without necessarily exiting the
|
|
// context (closing files, dropping AddrSpace, etc)
|
|
crate::syscall::process::exit_this_context(Some(excp), &mut token);
|
|
};
|
|
// TODO
|
|
/*
|
|
let Some(regs) = context.regs_mut() else {
|
|
// TODO: unhandled exception in this case too?
|
|
return;
|
|
};
|
|
let old_ip = regs.instr_pointer();
|
|
let old_archdep_reg = regs.ar
|
|
let (tctl, pctl, sigst) = context.sigcontrol().expect("already checked");
|
|
tctl.saved_ip.set(excp.rsp);
|
|
tctl.saved_archdep_reg*/
|
|
}
|