From 9fde94d068f87b34e0bc6bfa48388d89bb54f1e4 Mon Sep 17 00:00:00 2001 From: salmonik Date: Mon, 3 Aug 2026 02:23:35 +0200 Subject: v0.5.8: heap-free QR encoder + crash-dump QR on framebuffer MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Real hardware has no serial console, so a kernel crash only leaves a frozen screen. Every dump_lines() report now also renders the crash text as a scannable QR below the white-on-red band (photograph the screen instead of relaying text). - rsnix/src/qr.rs: from-scratch no_std/no_alloc byte-mode QR encoder (EC-L, v1..v40, .bss statics only — safe on the fault path): RS over GF(256), codeword interleave, zigzag placement, 8 masks with N1-N4 penalty auto-selection, format/version info. - gop.rs: dump_qr() joins dump lines into a static buffer, truncates with '...' at the v40-L capacity, encodes, and renders centered black-on-white with a 4-module quiet zone. - tools/qrcheck: host harness (embedds qr.rs) + qrcheck.py cross-check against segno's placement primitives. Verified bit-exact: 12 versions x 8 masks forced, plus auto-mask choice matches segno's penalty evaluation. Caught 3 encoder bugs (multi-block interleave offsets, transposed version info, mask eval ordering). - QEMU e2e: page-fault dump screendump decodes via zbarimg, content matches the serial log line-for-line. - test-msg.sh and test-fault.sh both PASS; debug+release 0 warnings. --- rsnix/src/gop.rs | 83 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 83 insertions(+) (limited to 'rsnix/src/gop.rs') diff --git a/rsnix/src/gop.rs b/rsnix/src/gop.rs index 012a9d0..ecf8fa9 100644 --- a/rsnix/src/gop.rs +++ b/rsnix/src/gop.rs @@ -516,6 +516,89 @@ pub fn dump_lines(lines: &[&str]) { } } } + // Scannable QR of the same text below the band (heap-free encoder; + // real hardware has no serial console — photograph the screen). + dump_qr(lines); +} + +/// Render the crash/fault lines as a QR code below the dump band. +/// +/// The joined text is built in a `.bss` buffer (the fault path must not +/// allocate) and encoded byte-mode EC-L by `crate::qr` (also heap-free, +/// static matrix). Over-long text is truncated with a "…" marker. If +/// the text does not fit a QR or there is no framebuffer, this is a no-op +/// and the band text remains the only channel. +static mut QR_TEXT: [u8; crate::qr::MAX_BYTES + 4] = [0; crate::qr::MAX_BYTES + 4]; + +pub fn dump_qr(lines: &[&str]) { + let Some(fb) = fb().as_ref() else { + return; + }; + unsafe { + let text = &mut *core::ptr::addr_of_mut!(QR_TEXT); + let mut n = 0usize; + for line in lines { + let b = line.as_bytes(); + let take = b.len().min(text.len() - n); + text[n..n + take].copy_from_slice(&b[..take]); + n += take; + if n < text.len() { + text[n] = b'\n'; + n += 1; + } + } + while n > 0 && text[n - 1] == b'\n' { + n -= 1; + } + if n == 0 { + return; + } + let cap = crate::qr::MAX_BYTES.saturating_sub(3); + if n > cap { + n = cap; + text[n..n + 3].copy_from_slice(b"..."); + n += 3; + } + if crate::qr::encode(&text[..n]) == 0 { + return; + } + } + render_qr(fb, lines.len() * 16); +} + +/// Draw the current QR matrix (black on white, quiet zone) centred below +/// the band, scaled to the largest square that fits. +fn render_qr(fb: &Framebuffer, band_px: usize) { + let size = crate::qr::size(); + if size == 0 { + return; + } + let total = size + 8; // 4-module quiet zone each side + let avail_w = fb.width.saturating_sub(24); + let avail_h = fb.height.saturating_sub(band_px + 16); + let scale = (avail_w / total).min(avail_h / total); + if scale < 1 { + return; + } + let px = total * scale; + let x0 = (fb.width - px) / 2; + let y0 = band_px + 8 + (avail_h.saturating_sub(px) / 2); + let white = rgb(0xFF, 0xFF, 0xFF); + let black = rgb(0, 0, 0); + fill_rect(x0, y0, px, px, white); + for y in 0..size { + for x in 0..size { + if crate::qr::module(x, y) { + fill_rect( + x0 + (x + 4) * scale, + y0 + (y + 4) * scale, + scale, + scale, + black, + ); + } + } + } } /// Read one pixel back from the shadow (RAM; cheap). Test/demo use only. -- cgit v1.2.3