diff options
Diffstat (limited to 'rsnix/src/interrupts.rs')
| -rw-r--r-- | rsnix/src/interrupts.rs | 41 |
1 files changed, 41 insertions, 0 deletions
diff --git a/rsnix/src/interrupts.rs b/rsnix/src/interrupts.rs index be252df..927e4f1 100644 --- a/rsnix/src/interrupts.rs +++ b/rsnix/src/interrupts.rs | |||
| @@ -393,6 +393,38 @@ pub(crate) struct IntFrame { | |||
| 393 | pub(crate) ss: u64, | 393 | pub(crate) ss: u64, |
| 394 | } | 394 | } |
| 395 | 395 | ||
| 396 | // --------------------------------------------------------------------------- | ||
| 397 | // Fault-dump re-entry guard | ||
| 398 | // --------------------------------------------------------------------------- | ||
| 399 | |||
| 400 | /// Set when `sched::fault_dump` is running. Any CPU exception that | ||
| 401 | /// fires *while the dump is already in progress* (typically because the | ||
| 402 | /// heap is corrupted and the dump path tripped over it) must never | ||
| 403 | /// recurse into the dump — that chain (#PF → #DF → #PF) is a triple | ||
| 404 | /// fault, which silently resets the machine with no report (black | ||
| 405 | /// screen, firmware splash — exactly what real hardware showed). The | ||
| 406 | /// guard short-circuits to a raw heap-free band and a hard halt. | ||
| 407 | static mut IN_DUMP: bool = false; | ||
| 408 | |||
| 409 | pub fn enter_dump() { | ||
| 410 | unsafe { | ||
| 411 | IN_DUMP = true; | ||
| 412 | } | ||
| 413 | } | ||
| 414 | |||
| 415 | fn in_dump() -> bool { | ||
| 416 | unsafe { IN_DUMP } | ||
| 417 | } | ||
| 418 | |||
| 419 | /// Raw, heap-free fallback: a fault fired inside the fault dump. | ||
| 420 | fn dump_guard_halt() -> ! { | ||
| 421 | crate::gop::dump_lines(&[ | ||
| 422 | "!!! fault inside fault dump", | ||
| 423 | "!!! halting (no triple fault)", | ||
| 424 | ]); | ||
| 425 | halt(); | ||
| 426 | } | ||
| 427 | |||
| 396 | /// Called from the asm common handler — dispatches to the right Rust | 428 | /// Called from the asm common handler — dispatches to the right Rust |
| 397 | /// handler and returns the frame pointer the asm should switch to (the | 429 | /// handler and returns the frame pointer the asm should switch to (the |
| 398 | /// next task's, or the same one when no switch happens). | 430 | /// next task's, or the same one when no switch happens). |
| @@ -433,9 +465,15 @@ unsafe extern "C" fn isr_dispatch(frame: &IntFrame) -> usize { | |||
| 433 | 8 => { | 465 | 8 => { |
| 434 | // Double fault — dump the frame (we're on the IST stack | 466 | // Double fault — dump the frame (we're on the IST stack |
| 435 | // so this is safe) and halt. | 467 | // so this is safe) and halt. |
| 468 | if in_dump() { | ||
| 469 | dump_guard_halt(); | ||
| 470 | } | ||
| 436 | crate::sched::fault_dump("double fault", 0, frame) | 471 | crate::sched::fault_dump("double fault", 0, frame) |
| 437 | } | 472 | } |
| 438 | 14 => { | 473 | 14 => { |
| 474 | if in_dump() { | ||
| 475 | dump_guard_halt(); | ||
| 476 | } | ||
| 439 | let cr2: u64; | 477 | let cr2: u64; |
| 440 | core::arch::asm!("mov {}, cr2", out(reg) cr2, | 478 | core::arch::asm!("mov {}, cr2", out(reg) cr2, |
| 441 | options(nostack, preserves_flags)); | 479 | options(nostack, preserves_flags)); |
| @@ -445,6 +483,9 @@ unsafe extern "C" fn isr_dispatch(frame: &IntFrame) -> usize { | |||
| 445 | v if v < 32 => { | 483 | v if v < 32 => { |
| 446 | // Any other CPU exception: full dump (screen + serial), | 484 | // Any other CPU exception: full dump (screen + serial), |
| 447 | // then halt the shell or abort the faulting background task. | 485 | // then halt the shell or abort the faulting background task. |
| 486 | if in_dump() { | ||
| 487 | dump_guard_halt(); | ||
| 488 | } | ||
| 448 | crate::sched::fault_dump("exception", 0, frame) | 489 | crate::sched::fault_dump("exception", 0, frame) |
| 449 | } | 490 | } |
| 450 | _ => fp, | 491 | _ => fp, |
