gioser

tawasuyu/brahman

Rust 496TOML 195Markdown 55JSON 197001 Definiciones

feat(renaser): pánico y OOM dejan testimonio por COM1

En pantalla solo cabe la franja roja: un grito breve, sin matiz. Para
diagnosticar colapsos en máquinas distintas a la del autor —donde no
es posible reproducirlos a mano—, los manejadores de fallo escriben
ahora una pista por COM1, además de pintar la franja.

- `baliza`: sumidero `Serie` que formatea sin tocar el heap, escribe a
  0x3F8 con espera acotada (antes mudo que colgar el sistema).
- El panic-handler vuelca el mensaje y la ubicación del `panic!`.
- El alloc-error-handler vuelca el `Layout` que reventó el techo.

QEMU con `-serial stdio` enruta COM1 a la terminal de `cargo run` — la
pista llega a quien lanzó el kernel, aunque la pantalla esté en negro.

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>

d85bb3819ef403c7e4414563a45663b064fcfef0 · por sergio · (hace 5 meses) · padre 8787b0566 · ver el árbol en este commit

Delta semántico · 7

renaser/kernel/src/baliza.rs

1 archivos · +69 −5

diff --git a/renaser/kernel/src/baliza.rs b/renaser/kernel/src/baliza.rs
index 01b841e..5063eb4 100644
--- a/renaser/kernel/src/baliza.rs
+++ b/renaser/kernel/src/baliza.rs
@@ -7,12 +7,54 @@
 //  una franja de advertencia incluso cuando el resto del kernel ya no es fiable.
 // =============================================================================
 
+use core::fmt::Write;
 use core::panic::PanicInfo;
 use core::ptr;
 use core::sync::atomic::{AtomicPtr, AtomicU32, AtomicUsize, Ordering};
 
 use crate::grafico::{escribir_pixel_volatil, Pantalla};
 
+// =============================================================================
+//  TESTIMONIO POR EL PUERTO SERIE — para diagnosticar colapsos sin pantalla
+// -----------------------------------------------------------------------------
+//  En pantalla solo cabe la franja roja: un grito breve, sin matiz. Pero los
+//  manejadores de fallo escriben TAMBIEN al puerto serie COM1 —que QEMU enruta
+//  a la terminal de `cargo run` con `-serial stdio`—. Asi, cuando el kernel
+//  colapsa fuera del Proxmox del autor, deja una pista legible de la causa.
+// =============================================================================
+
+/// Puerto de datos de COM1.
+const SERIE_DATOS: u16 = 0x3F8;
+/// Registro de estado de linea de COM1 — bit 5: el transmisor esta libre.
+const SERIE_LSR: u16 = 0x3FD;
+
+/// Envia un byte por COM1, con una espera acotada por si el firmware no nos lo
+/// dejo configurado. Si se agota la paciencia, calla — antes mudo que cuelgue.
+fn serie_escribir(byte: u8) {
+    for _ in 0..1_000_000 {
+        // SEGURIDAD: 0x3FD es el registro de estado de linea de COM1, fijo en
+        // la arquitectura PC; leerlo es inocuo.
+        let lsr = unsafe { x86_64::instructions::port::Port::<u8>::new(SERIE_LSR).read() };
+        if lsr & 0x20 != 0 {
+            // SEGURIDAD: 0x3F8 es el puerto de datos de COM1.
+            unsafe { x86_64::instructions::port::Port::<u8>::new(SERIE_DATOS).write(byte) };
+            return;
+        }
+    }
+}
+
+/// Sumidero de impresion al puerto serie — formatea sin tocar el heap.
+struct Serie;
+
+impl Write for Serie {
+    fn write_str(&mut self, s: &str) -> core::fmt::Result {
+        for &b in s.as_bytes() {
+            serie_escribir(b);
+        }
+        Ok(())
+    }
+}
+
 /// Datos del framebuffer expuestos a los manejadores de fallo. Todo son
 /// atomicos: la baliza es intrinsecamente `Sync`, sin cerrojos.
 pub(crate) struct BalizaPanico {
@@ -102,27 +144,49 @@ pub(crate) static BALIZA_PANICO: BalizaPanico = BalizaPanico::apagada();
 //  MANEJADORES DE FALLO — cuando el sistema colapsa, lo DIBUJA
 // =============================================================================
 
-/// Si renaser colapsa, tatuamos una franja ROJA en lo alto del framebuffer.
+/// Si renaser colapsa, tatuamos una franja ROJA en lo alto del framebuffer Y
+/// dejamos por COM1 un testimonio del panico: su mensaje, su lugar — la pista
+/// que en pantalla no cabe.
 #[panic_handler]
-fn al_colapsar(_info: &PanicInfo) -> ! {
+fn al_colapsar(info: &PanicInfo) -> ! {
     x86_64::instructions::interrupts::disable();
     BALIZA_PANICO.pintar_banda(
         0,
         BALIZA_PANICO.franja(),
         BALIZA_PANICO.pixel_alerta.load(Ordering::Relaxed),
     );
+    let _ = writeln!(Serie);
+    let _ = writeln!(Serie, "*** renaser :: panico ***");
+    if let Some(lugar) = info.location() {
+        let _ = writeln!(
+            Serie,
+            "  en {}:{}:{}",
+            lugar.file(),
+            lugar.line(),
+            lugar.column()
+        );
+    }
+    let _ = writeln!(Serie, "  {}", info.message());
     crate::detener()
 }
 
-/// Si el heap se agota, tatuamos una franja NARANJA: un fallo distinto al
-/// colapso, y distinguible de un vistazo.
+/// Si el heap se agota, tatuamos una franja NARANJA y dejamos en el serie la
+/// disposicion que reviento el techo: tamaño y alineamiento.
 #[alloc_error_handler]
-fn al_agotar_memoria(_disposicion: core::alloc::Layout) -> ! {
+fn al_agotar_memoria(disposicion: core::alloc::Layout) -> ! {
     x86_64::instructions::interrupts::disable();
     BALIZA_PANICO.pintar_banda(
         0,
         BALIZA_PANICO.franja(),
         BALIZA_PANICO.pixel_oom.load(Ordering::Relaxed),
     );
+    let _ = writeln!(Serie);
+    let _ = writeln!(Serie, "*** renaser :: agotamiento de memoria ***");
+    let _ = writeln!(
+        Serie,
+        "  layout: tamaño={} alineamiento={}",
+        disposicion.size(),
+        disposicion.align()
+    );
     crate::detener()
 }