diff options
| author | salmonik <salmonik@tuta.io> | 2026-08-02 15:21:34 +0200 |
|---|---|---|
| committer | salmonik <salmonik@tuta.io> | 2026-08-02 15:21:34 +0200 |
| commit | c7d92071e1dd11c45999457b16554b4abaa73b28 (patch) | |
| tree | aff7c7f19adcffe1dd91e1c2514af02c77fd524e /rsnix/src/interrupts.rs | |
| parent | v0.5.5: fix real-hardware boot crash — shadow back in heap as first allocation (diff) | |
| download | rs-dos-c7d92071e1dd11c45999457b16554b4abaa73b28.tar.gz rs-dos-c7d92071e1dd11c45999457b16554b4abaa73b28.zip | |
v0.5.5: crash reports go to a dedicated CRASHLOG USB stick
The fault handler now writes crash.log to the volume labeled CRASHLOG
(label-selected via fat::crash_vol), falling back to slot 0. On real
hardware the boot USB may be unwritable, so a second stick labeled
CRASHLOG survives the write; the shell auto-prints the previous crash
report from it at next boot (read_crash_log resolves /disk/<uid>/crash.log
through the FAT path, since Disk nodes hold no cached children).
- fat.rs: volume_label() + crash_vol() (no allocation)
- fs.rs: write_crash_log targets crash_vol(); read_crash_log prefers the
CRASHLOG disk node; drop now-unused first_disk()
- mkdisk.sh: --label option
- test-fault.sh: boots with a dedicated CRASHLOG stick (mkfs -F 32 -n
CRASHLOG), verifies crash.log lands there and NOT on the ESP
Diffstat (limited to '')
| -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, |
