From d5f41d6e057300c82b83653b4afadeffb45a9f19 Mon Sep 17 00:00:00 2001 From: salmonik Date: Mon, 3 Aug 2026 01:34:31 +0200 Subject: v0.5.7: fix vt verify content check — expect BLANK (0x70020), not zero MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The pre-verify 0x70020 'mystery' was a debug artifact, not corruption: BLANK = {ch:32, fg:7, bg:0} = raw 0x70020, and vec![BLANK; n] fills both grids with it at init. The old check compared cells against 0 and its probe loop break-ed on the first non-zero cell, so probes 1-3 stayed at their zero-initialized slots — c=[0x70020,0,0,0] was bogus on every healthy boot. A full-grid dump proves QEMU grids are 100% BLANK at shell entry (nz=3700 blank=3700). Fix: probes compare against blank_raw (0x70020), record all four values (no early break), probe both cells and rendered, and add a full-grid nz/blank summary line. The OptiPlex firmware 0x00AAAAAA scrub remains cleanly distinguishable (scrub != blank). Markers: the shell banner occupies grid rows 0-1 and the prompt row 2 (proven by screendump), and a stale crash.log replays at rows 2+, so the shadow and vt markers moved to the bottom of the screen via the new gop::text_rows()/bottom_row(off) helpers instead of rows 1-2. QEMU verified: verify: ok every boot; test-msg + test-fault PASS; debug and release builds 0 warnings. --- rsnix/src/gop.rs | 44 ++++++++++++++++++++++++++++++++++++-------- 1 file changed, 36 insertions(+), 8 deletions(-) (limited to 'rsnix/src/gop.rs') diff --git a/rsnix/src/gop.rs b/rsnix/src/gop.rs index 8e804e5..012a9d0 100644 --- a/rsnix/src/gop.rs +++ b/rsnix/src/gop.rs @@ -293,13 +293,16 @@ pub fn init(gop: &mut GraphicsOutputProtocol) { .map(|c| core::str::from_utf8(c).unwrap_or("?")) .collect(); dump_lines(&lines); - crate::gop::boot_marker("gop: NO-SHADOW (fallback mode)"); + crate::gop::boot_marker_at("gop: NO-SHADOW (fallback)", bottom_row(0)); } else { - crate::gop::boot_marker(&alloc::format!( - "gop: shadow {:#x} {:.2} MiB", - SHADOW as usize, - swords as f64 * 4.0 / 1048576.0 - )); + crate::gop::boot_marker_at( + &alloc::format!( + "gop: shadow {:#x} {:.2} MiB", + SHADOW as usize, + swords as f64 * 4.0 / 1048576.0 + ), + bottom_row(0), + ); } } } @@ -313,6 +316,16 @@ pub fn init(gop: &mut GraphicsOutputProtocol) { /// direct-display glyph path (the shadow may not exist yet in early /// boot); the shell banner overwrites row 0 at first paint. pub fn boot_marker(msg: &str) { + boot_marker_at(msg, 0); +} + +/// boot_marker at an explicit text row. The shell's first paint +/// occupies grid rows 0 (motd) and 1 (=== banner) and the prompt sits +/// at row 2 — markers whose *values* matter after boot (shadow state, +/// vt grid pointers) are anchored to the bottom of the screen via +/// `bottom_row`, which also survives a stale crash.log replay (the +/// fault band is ~20 lines starting at grid row 2). +pub fn boot_marker_at(msg: &str, row: usize) { // Record the stage so a later panic band can show how far boot got. unsafe { let n = msg.len().min(STAGE_MAX); @@ -323,24 +336,39 @@ pub fn boot_marker(msg: &str) { let Some(fb) = fb().as_ref() else { return; }; + if row >= fb.height / 16 { + return; + } let cols = fb.width / 8; let white = (0xFFu8, 0xFFu8, 0xFFu8); let black = (0x00u8, 0x00u8, 0x00u8); let mut c = 0usize; for &b in msg.as_bytes().iter().take(cols) { if b < 0x80 { - blit_glyph(c, 0, b as u16, white, black); + blit_glyph(c, row, b as u16, white, black); c += 1; } } // Blank the rest of the row so a shorter message can't leave stale // glyphs behind. while c < cols { - blit_glyph(c, 0, b' ' as u16, white, black); + blit_glyph(c, row, b' ' as u16, white, black); c += 1; } } +/// Text-row count of the active framebuffer (each row is 16 px tall). +pub fn text_rows() -> usize { + fb().as_ref().map(|f| f.height / 16).unwrap_or(0) +} + +/// Row index of the `off`-th text row from the bottom (0 = last row). +/// Bottom-anchored markers survive the shell banner + prompt at the top +/// and a stale crash.log replay below it. +pub fn bottom_row(off: usize) -> usize { + text_rows().saturating_sub(1 + off) +} + // --------------------------------------------------------------------------- // Public text API — the text surface now lives in `crate::vt`; these wrappers // keep callers (shell, macros) unchanged. -- cgit v1.2.3