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/sched.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 'rsnix/src/sched.rs')
| -rw-r--r-- | rsnix/src/sched.rs | 99 |
1 files changed, 74 insertions, 25 deletions
diff --git a/rsnix/src/sched.rs b/rsnix/src/sched.rs index f48e210..a195d1c 100644 --- a/rsnix/src/sched.rs +++ b/rsnix/src/sched.rs | |||
| @@ -28,9 +28,7 @@ use core::fmt; | |||
| 28 | use core::sync::atomic::{AtomicBool, AtomicU64, Ordering}; | 28 | use core::sync::atomic::{AtomicBool, AtomicU64, Ordering}; |
| 29 | 29 | ||
| 30 | extern crate alloc; | 30 | extern crate alloc; |
| 31 | use alloc::format; | ||
| 32 | use alloc::string::String; | 31 | use alloc::string::String; |
| 33 | use alloc::string::ToString; | ||
| 34 | use alloc::vec::Vec; | 32 | use alloc::vec::Vec; |
| 35 | 33 | ||
| 36 | use crate::interrupts::IntFrame; | 34 | use crate::interrupts::IntFrame; |
| @@ -40,7 +38,7 @@ use crate::paging; | |||
| 40 | pub const MAX_TASKS: usize = 16; | 38 | pub const MAX_TASKS: usize = 16; |
| 41 | pub const SHELL_TID: usize = 0; | 39 | pub const SHELL_TID: usize = 0; |
| 42 | pub const IDLE_TID: usize = 1; | 40 | pub const IDLE_TID: usize = 1; |
| 43 | const STACK_PAGES: usize = 3; // 1 guard page + 8 KiB stack | 41 | const STACK_PAGES: usize = 9; // 1 guard page + 32 KiB stack — faults dump on the task's own stack |
| 44 | const NAME_MAX: usize = 24; | 42 | const NAME_MAX: usize = 24; |
| 45 | const PANIC_MAX: usize = 64; | 43 | const PANIC_MAX: usize = 64; |
| 46 | 44 | ||
| @@ -556,8 +554,15 @@ pub fn abort_current(msg: &str) -> ! { | |||
| 556 | /// Real hardware has no serial console, so the screen dump is what lets | 554 | /// Real hardware has no serial console, so the screen dump is what lets |
| 557 | /// a user report a crash precisely — vector, fault address, RIP and task. | 555 | /// a user report a crash precisely — vector, fault address, RIP and task. |
| 558 | /// Runs with interrupts disabled (we're inside the ISR). | 556 | /// Runs with interrupts disabled (we're inside the ISR). |
| 557 | /// | ||
| 558 | /// Heap-free by construction: the dump's own `alloc::format!` used to | ||
| 559 | /// allocate from the heap, and when the underlying fault *was* heap | ||
| 560 | /// corruption the reporter itself died (#PF → #DF → triple fault → | ||
| 561 | /// silent reboot). Everything here formats into stack buffers, and the | ||
| 562 | /// ISR `in_dump` guard halts instead of recursing if even that faults. | ||
| 559 | #[allow(clippy::needless_return)] | 563 | #[allow(clippy::needless_return)] |
| 560 | pub(crate) fn fault_dump(kind: &str, cr2: u64, frame: &crate::interrupts::IntFrame) -> ! { | 564 | pub(crate) fn fault_dump(kind: &str, cr2: u64, frame: &crate::interrupts::IntFrame) -> ! { |
| 565 | crate::interrupts::enter_dump(); | ||
| 561 | let vector = frame.vector; | 566 | let vector = frame.vector; |
| 562 | let error = frame.error; | 567 | let error = frame.error; |
| 563 | let rip = frame.rip; | 568 | let rip = frame.rip; |
| @@ -565,35 +570,79 @@ pub(crate) fn fault_dump(kind: &str, cr2: u64, frame: &crate::interrupts::IntFra | |||
| 565 | let rsp = frame.rsp; | 570 | let rsp = frame.rsp; |
| 566 | let id = unsafe { CURRENT }; | 571 | let id = unsafe { CURRENT }; |
| 567 | let name = name_str(id); | 572 | let name = name_str(id); |
| 568 | let name_buf = name.to_string(); | ||
| 569 | let in_kernel = unsafe { CURRENT } <= IDLE_TID; | 573 | let in_kernel = unsafe { CURRENT } <= IDLE_TID; |
| 570 | 574 | ||
| 571 | // Serial: a full, parseable line. | 575 | // Serial: a full, parseable line (stack-formatted). |
| 572 | crate::serial_println!( | 576 | let mut buf = [0u8; 176]; |
| 573 | "!!! {} vec={} err={:#x} rip={:#x} cr2={:#x} rsp={:#x} rflags={:#x} task={} \"{}\"", | 577 | let n = { |
| 574 | kind, | 578 | let mut w = crate::strbuf::FmtBuf::new(&mut buf); |
| 575 | vector, | 579 | let _ = fmt::write( |
| 576 | error, | 580 | &mut w, |
| 577 | rip, | 581 | core::format_args!( |
| 578 | cr2, | 582 | "!!! {} vec={} err={:#x} rip={:#x} cr2={:#x} rsp={:#x} rflags={:#x} task={} \"{}\"", |
| 579 | rsp, | 583 | kind, |
| 580 | rflags, | 584 | vector, |
| 581 | id, | 585 | error, |
| 582 | name_buf | 586 | rip, |
| 583 | ); | 587 | cr2, |
| 584 | crate::serial_println!("!!! halting (shell task fault — machine stops)"); | 588 | rsp, |
| 585 | 589 | rflags, | |
| 586 | // Screen: white-on-red band via the clamped raw dump path. | 590 | id, |
| 587 | let l1 = alloc::format!("!!! {} vec={} err={:#x}", kind, vector, error); | 591 | name |
| 588 | let l2 = alloc::format!("rip={:#x} cr2={:#x}", rip, cr2); | 592 | ), |
| 589 | let l3 = alloc::format!("rsp={:#x} task={} {}", rsp, id, name_buf); | 593 | ); |
| 590 | let lines = [l1.as_str(), l2.as_str(), l3.as_str()]; | 594 | w.len() |
| 595 | }; | ||
| 596 | let line = core::str::from_utf8(&buf[..n]).unwrap_or("!!! dump"); | ||
| 597 | crate::serial::write_str(line); | ||
| 598 | crate::serial::write_str("\r\n!!! halting (shell task fault — machine stops)\r\n"); | ||
| 599 | |||
| 600 | // Screen: white-on-red band via the clamped raw dump path (heap-free). | ||
| 601 | let mut b1 = [0u8; 72]; | ||
| 602 | let mut b2 = [0u8; 72]; | ||
| 603 | let mut b3 = [0u8; 96]; | ||
| 604 | let n1 = { | ||
| 605 | let mut w = crate::strbuf::FmtBuf::new(&mut b1); | ||
| 606 | let _ = fmt::write( | ||
| 607 | &mut w, | ||
| 608 | core::format_args!("!!! {} vec={} err={:#x}", kind, vector, error), | ||
| 609 | ); | ||
| 610 | w.len() | ||
| 611 | }; | ||
| 612 | let n2 = { | ||
| 613 | let mut w = crate::strbuf::FmtBuf::new(&mut b2); | ||
| 614 | let _ = fmt::write(&mut w, core::format_args!("rip={:#x} cr2={:#x}", rip, cr2)); | ||
| 615 | w.len() | ||
| 616 | }; | ||
| 617 | let n3 = { | ||
| 618 | let mut w = crate::strbuf::FmtBuf::new(&mut b3); | ||
| 619 | let _ = fmt::write( | ||
| 620 | &mut w, | ||
| 621 | core::format_args!("rsp={:#x} task={} {}", rsp, id, name), | ||
| 622 | ); | ||
| 623 | w.len() | ||
| 624 | }; | ||
| 625 | let l1 = core::str::from_utf8(&b1[..n1]).unwrap_or("!!!"); | ||
| 626 | let l2 = core::str::from_utf8(&b2[..n2]).unwrap_or(""); | ||
| 627 | let l3 = core::str::from_utf8(&b3[..n3]).unwrap_or(""); | ||
| 628 | let lines = [l1, l2, l3]; | ||
| 591 | crate::gop::dump_lines(&lines); | 629 | crate::gop::dump_lines(&lines); |
| 592 | 630 | ||
| 631 | // Best-effort: persist the report to the disk so it survives the | ||
| 632 | // reboot on real hardware (no serial console). Any fault here is | ||
| 633 | // caught by the in_dump guard and halts. | ||
| 634 | crate::fs::write_crash_log(&lines); | ||
| 635 | |||
| 593 | if in_kernel { | 636 | if in_kernel { |
| 594 | crate::interrupts::halt() | 637 | crate::interrupts::halt() |
| 595 | } else { | 638 | } else { |
| 596 | abort_current(&format!("{} vec={}", kind, vector)) | 639 | let mut mb = [0u8; 48]; |
| 640 | let mn = { | ||
| 641 | let mut w = crate::strbuf::FmtBuf::new(&mut mb); | ||
| 642 | let _ = fmt::write(&mut w, core::format_args!("{} vec={}", kind, vector)); | ||
| 643 | w.len() | ||
| 644 | }; | ||
| 645 | abort_current(core::str::from_utf8(&mb[..mn]).unwrap_or("fault")) | ||
| 597 | } | 646 | } |
| 598 | } | 647 | } |
| 599 | 648 | ||
