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>
This commit is contained in:
@@ -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()
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user