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::::new(SERIE_LSR).read() }; + if lsr & 0x20 != 0 { + // SEGURIDAD: 0x3F8 es el puerto de datos de COM1. + unsafe { x86_64::instructions::port::Port::::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() }