summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--rsdos/src/main.rs16
-rw-r--r--rsnix/src/gop.rs21
-rw-r--r--rsnix/src/vt.rs20
3 files changed, 55 insertions, 2 deletions
diff --git a/rsdos/src/main.rs b/rsdos/src/main.rs
index b6393ee..aa116e4 100644
--- a/rsdos/src/main.rs
+++ b/rsdos/src/main.rs
@@ -5,6 +5,8 @@
5use core::panic::PanicInfo; 5use core::panic::PanicInfo;
6use rsnix::uefi::*; 6use rsnix::uefi::*;
7 7
8extern crate alloc;
9
8// --------------------------------------------------------------------------- 10// ---------------------------------------------------------------------------
9// Panic handler 11// Panic handler
10// --------------------------------------------------------------------------- 12// ---------------------------------------------------------------------------
@@ -47,9 +49,11 @@ fn panic(info: &PanicInfo) -> ! {
47 } 49 }
48 // Real hardware has no serial console — put the panic on the screen 50 // Real hardware has no serial console — put the panic on the screen
49 // too (white-on-red band), so a pre-shell panic is not a silent 51 // too (white-on-red band), so a pre-shell panic is not a silent
50 // blank screen. 52 // blank screen. The last boot stage is appended so the user can
53 // see how far boot got before the crash.
51 let mut loc_buf = [0u8; 96]; 54 let mut loc_buf = [0u8; 96];
52 let mut msg_buf = [0u8; 128]; 55 let mut msg_buf = [0u8; 128];
56 let mut stage_buf = [0u8; 96];
53 let loc_n = { 57 let loc_n = {
54 let mut w = rsnix::strbuf::FmtBuf::new(&mut loc_buf); 58 let mut w = rsnix::strbuf::FmtBuf::new(&mut loc_buf);
55 if let Some(loc) = info.location() { 59 if let Some(loc) = info.location() {
@@ -64,10 +68,17 @@ fn panic(info: &PanicInfo) -> ! {
64 let _ = write!(&mut w, "{}", info.message()); 68 let _ = write!(&mut w, "{}", info.message());
65 w.len() 69 w.len()
66 }; 70 };
71 let stage_n = {
72 let s = rsnix::gop::last_boot_stage();
73 let n = s.len().min(stage_buf.len());
74 stage_buf[..n].copy_from_slice(&s.as_bytes()[..n]);
75 n
76 };
67 let lines = [ 77 let lines = [
68 "*** PANIC ***", 78 "*** PANIC ***",
69 core::str::from_utf8(&msg_buf[..msg_n]).unwrap_or("?"), 79 core::str::from_utf8(&msg_buf[..msg_n]).unwrap_or("?"),
70 core::str::from_utf8(&loc_buf[..loc_n]).unwrap_or("?"), 80 core::str::from_utf8(&loc_buf[..loc_n]).unwrap_or("?"),
81 core::str::from_utf8(&stage_buf[..stage_n]).unwrap_or(""),
71 ]; 82 ];
72 rsnix::gop::dump_lines(&lines); 83 rsnix::gop::dump_lines(&lines);
73 loop { 84 loop {
@@ -150,7 +161,8 @@ pub unsafe extern "efiapi" fn efi_main(image_handle: Handle, system_table: *mut
150 rsnix::vt::init(); 161 rsnix::vt::init();
151 let (rows, cols) = rsnix::vt::size(); 162 let (rows, cols) = rsnix::vt::size();
152 rsnix::serial_println!("vt: terminal ready ({}x{})", rows, cols); 163 rsnix::serial_println!("vt: terminal ready ({}x{})", rows, cols);
153 rsnix::gop::boot_marker("rsnix: gop+vt ok"); 164 let (fw, fh) = rsnix::gop::pixel_dims();
165 rsnix::gop::boot_marker(&alloc::format!("rsnix: gop {}x{} vt {}x{} ok", fw, fh, cols, rows));
154 166
155 // ExitBootServices — we now own the machine 167 // ExitBootServices — we now own the machine
156 if unsafe { (bs.exit_boot_services)(image_handle, map_key) } != EFI_SUCCESS { 168 if unsafe { (bs.exit_boot_services)(image_handle, map_key) } != EFI_SUCCESS {
diff --git a/rsnix/src/gop.rs b/rsnix/src/gop.rs
index 7b97d14..90c24bf 100644
--- a/rsnix/src/gop.rs
+++ b/rsnix/src/gop.rs
@@ -71,6 +71,20 @@ pub fn framebuffer_region() -> Option<(usize, usize)> {
71static mut SHADOW: *mut u32 = core::ptr::null_mut(); 71static mut SHADOW: *mut u32 = core::ptr::null_mut();
72static mut SHADOW_WORDS: usize = 0; 72static mut SHADOW_WORDS: usize = 0;
73 73
74/// Last boot-progress stage (set by `boot_marker`); the panic handler
75/// appends it to the on-screen band so a crash shows where boot died.
76static mut LAST_BOOT_STAGE: [u8; 96] = [0; 96];
77static mut LAST_BOOT_STAGE_LEN: usize = 0;
78const STAGE_MAX: usize = 96;
79
80/// The last boot stage string (for the panic band).
81pub fn last_boot_stage() -> alloc::string::String {
82 unsafe {
83 alloc::string::String::from_utf8_lossy(&LAST_BOOT_STAGE[..LAST_BOOT_STAGE_LEN])
84 .into_owned()
85 }
86}
87
74/// Diagnostic: when > 0, every display store busy-waits ~this many 88/// Diagnostic: when > 0, every display store busy-waits ~this many
75/// cycles first, simulating the per-write bus cost of an uncached real 89/// cycles first, simulating the per-write bus cost of an uncached real
76/// GOP framebuffer (QEMU's framebuffer is RAM — instant). Lets us 90/// GOP framebuffer (QEMU's framebuffer is RAM — instant). Lets us
@@ -338,6 +352,13 @@ pub fn init_shadow() {
338/// direct-display glyph path (the shadow may not exist yet in early 352/// direct-display glyph path (the shadow may not exist yet in early
339/// boot); the shell banner overwrites row 0 at first paint. 353/// boot); the shell banner overwrites row 0 at first paint.
340pub fn boot_marker(msg: &str) { 354pub fn boot_marker(msg: &str) {
355 // Record the stage so a later panic band can show how far boot got.
356 unsafe {
357 let n = msg.len().min(STAGE_MAX);
358 let dst = core::ptr::addr_of_mut!(LAST_BOOT_STAGE) as *mut u8;
359 core::ptr::copy_nonoverlapping(msg.as_ptr(), dst, n);
360 LAST_BOOT_STAGE_LEN = n;
361 }
341 let Some(fb) = fb().as_ref() else { 362 let Some(fb) = fb().as_ref() else {
342 return; 363 return;
343 }; 364 };
diff --git a/rsnix/src/vt.rs b/rsnix/src/vt.rs
index 53821b7..4578f6c 100644
--- a/rsnix/src/vt.rs
+++ b/rsnix/src/vt.rs
@@ -96,6 +96,26 @@ static mut CUR: usize = 0;
96/// Allocate the cell grid. Call once after `gop::init()`. 96/// Allocate the cell grid. Call once after `gop::init()`.
97pub fn init() { 97pub fn init() {
98 let (cols, rows) = gop::dims(); 98 let (cols, rows) = gop::dims();
99 if rows == 0 || cols == 0 {
100 // No usable framebuffer geometry: GOP is missing entirely or the
101 // active mode reports 0x0. Without a display there is nothing
102 // the shell can do, so stop with a visible explanation instead
103 // of building an empty grid and panicking at the first cell
104 // write (a blank screen on real hardware, which has no serial
105 // console).
106 let lines = [
107 "FATAL: no framebuffer geometry",
108 "GOP missing or active mode is 0x0",
109 "see the 'gop:' boot marker / serial log",
110 ];
111 crate::gop::dump_lines(&lines);
112 crate::serial_println!("FATAL: no framebuffer geometry (GOP missing or 0x0 mode)");
113 loop {
114 unsafe {
115 core::arch::asm!("cli; hlt");
116 }
117 }
118 }
99 unsafe { 119 unsafe {
100 ROWS = rows; 120 ROWS = rows;
101 COLS = cols; 121 COLS = cols;