Skip to content

Commit

Permalink
feat(hal_x86_64): be less stupid about ISR tracing (#496)
Browse files Browse the repository at this point in the history
Tracing in interrupt service routines can easily deadlock the kernel
(c.f. c4183db). Let's be less dumb
about it.

This commit does the following:

- Factor out the code for forcefully unlocking the mutices around
tracing outputs (VGA and serial) into a separate, unsafe function, and
make the ISRs that are (currently) always a kernel oops just call that
- Remove the trace in the "spurious interrupt" handler, as that seems
like a good way to deadlock the universe. In future, let's have a way to
track whether spurious interrupts have fired that doesn't involve
emitting a trace (maybe counters?)
  • Loading branch information
hawkw authored Dec 30, 2024
1 parent c4183db commit e23a00d
Showing 1 changed file with 39 additions and 22 deletions.
61 changes: 39 additions & 22 deletions hal-x86_64/src/interrupt.rs
Original file line number Diff line number Diff line change
Expand Up @@ -523,11 +523,10 @@ impl hal_core::interrupt::Control for Idt {
code: u64,
) {
unsafe {
// Safety: who cares!
crate::vga::writer().force_unlock();
if let Some(com1) = crate::serial::com1() {
com1.force_unlock();
}
// Safety: An invalid TSS exception is always an oops. Since we're
// not coming back from this, it's okay to forcefully unlock the
// tracing outputs.
force_unlock_tracing();
}
let selector = SelectorErrorCode(code as u16);
tracing::error!(?selector, "invalid task-state segment!");
Expand All @@ -548,11 +547,10 @@ impl hal_core::interrupt::Control for Idt {
code: u64,
) {
unsafe {
// Safety: who cares!
crate::vga::writer().force_unlock();
if let Some(com1) = crate::serial::com1() {
com1.force_unlock();
}
// Safety: An segment not present exception is always an oops.
// Since we're not coming back from this, it's okay to
// forcefully unlock the tracing outputs.
force_unlock_tracing();
}
let selector = SelectorErrorCode(code as u16);
tracing::error!(?selector, "a segment was not present!");
Expand All @@ -573,11 +571,10 @@ impl hal_core::interrupt::Control for Idt {
code: u64,
) {
unsafe {
// Safety: who cares!
crate::vga::writer().force_unlock();
if let Some(com1) = crate::serial::com1() {
com1.force_unlock();
}
// Safety: An stack-segment fault exeption is always an oops.
// Since we're not coming back from this, it's okay to
// forcefully unlock the tracing outputs.
force_unlock_tracing();
}
let selector = SelectorErrorCode(code as u16);
tracing::error!(?selector, "a stack-segment fault is happening");
Expand All @@ -598,12 +595,17 @@ impl hal_core::interrupt::Control for Idt {
code: u64,
) {
unsafe {
// Safety: who cares!

crate::vga::writer().force_unlock();
if let Some(com1) = crate::serial::com1() {
com1.force_unlock();
}
// Safety: A general protection fault is (currently) always an
// oops. Since we're not coming back from this, it's okay to
// forcefully unlock the tracing outputs.
//
// TODO(eliza): in the future, if we allow the kernel to
// recover from general protection faults in user mode programs,
// rather than treating them as invariably fatal, we should
// probably not always do this. Instead, we should just handle
// the user-mode GPF non-fatally, and only unlock the tracing
// stuff if we know we're going to do a kernel oops...
force_unlock_tracing();
}

let segment = if code > 0 {
Expand All @@ -625,7 +627,7 @@ impl hal_core::interrupt::Control for Idt {
}

extern "x86-interrupt" fn spurious_isr() {
tracing::trace!("spurious");
// TODO(eliza): do we need to actually do something here?
}

// === exceptions ===
Expand Down Expand Up @@ -675,6 +677,21 @@ impl hal_core::interrupt::Control for Idt {
}
}

/// Forcefully unlock the VGA port and COM1 serial port (used by tracing), so
/// that an ISR can log stuff without deadlocking.
///
/// # Safety
///
/// This forcefully unlocks a mutex, which is probably bad to do. Only do this
/// in ISRs that definitely represent real actual faults, and not just because
/// "you wanted to log something".
unsafe fn force_unlock_tracing() {
crate::vga::writer().force_unlock();
if let Some(com1) = crate::serial::com1() {
com1.force_unlock();
}
}

impl fmt::Debug for Registers {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
let Self {
Expand Down

0 comments on commit e23a00d

Please sign in to comment.