Files
RedBear-OS/src/context/signal.rs
T
vasilito 573b3e6eae fix: handle early-boot exceptions in excp_handler gracefully
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.
2026-07-02 22:24:23 +03:00

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*/
}