tawasuyu

tawasuyu/brahman

Rust 521TOML 202Markdown 57JSON 197505 Definiciones

⎇ 71ebcea899d0ac71f959ad5461d830d952087735 renaser/kernel/src/baliza.rs Rust · 194 líneas · 7.5 KB Historia Quién tocó qué crudo

diag(renaser): trazado por serie del arranque + DMA salta la página 0 89117f47c · sergio ·

1// =============================================================================
2//  renaser :: kernel/src/baliza.rs — la red de seguridad visual del sistema
3// -----------------------------------------------------------------------------
4//  Sin consola de texto que valga cuando el sistema cae: si renaser colapsa, lo
5//  DIBUJA. La baliza publica —de forma atomica y sin cerrojos— los datos
6//  minimos del framebuffer, de modo que los manejadores de fallo puedan pintar
7//  una franja de advertencia incluso cuando el resto del kernel ya no es fiable.
8// =============================================================================
9
10use core::fmt::Write;
11use core::panic::PanicInfo;
12use core::ptr;
13use core::sync::atomic::{AtomicPtr, AtomicU32, AtomicUsize, Ordering};
14
15use crate::grafico::{escribir_pixel_volatil, Pantalla};
16
17// =============================================================================
18//  TESTIMONIO POR EL PUERTO SERIE — para diagnosticar colapsos sin pantalla
19// -----------------------------------------------------------------------------
20//  En pantalla solo cabe la franja roja: un grito breve, sin matiz. Pero los
21//  manejadores de fallo escriben TAMBIEN al puerto serie COM1 —que QEMU enruta
22//  a la terminal de `cargo run` con `-serial stdio`—. Asi, cuando el kernel
23//  colapsa fuera del Proxmox del autor, deja una pista legible de la causa.
24// =============================================================================
25
26/// Puerto de datos de COM1.
27const SERIE_DATOS: u16 = 0x3F8;
28/// Registro de estado de linea de COM1 — bit 5: el transmisor esta libre.
29const SERIE_LSR: u16 = 0x3FD;
30
31/// Envia un byte por COM1, con una espera acotada por si el firmware no nos lo
32/// dejo configurado. Si se agota la paciencia, calla — antes mudo que cuelgue.
33fn serie_escribir(byte: u8) {
34    for _ in 0..1_000_000 {
35        // SEGURIDAD: 0x3FD es el registro de estado de linea de COM1, fijo en
36        // la arquitectura PC; leerlo es inocuo.
37        let lsr = unsafe { x86_64::instructions::port::Port::<u8>::new(SERIE_LSR).read() };
38        if lsr & 0x20 != 0 {
39            // SEGURIDAD: 0x3F8 es el puerto de datos de COM1.
40            unsafe { x86_64::instructions::port::Port::<u8>::new(SERIE_DATOS).write(byte) };
41            return;
42        }
43    }
44}
45
46/// Sumidero de impresion al puerto serie — formatea sin tocar el heap. Publico
47/// para que cualquier modulo del kernel pueda dejar trazas en COM1 con un
48/// simple `writeln!(crate::baliza::Serie, "...", ...)`.
49pub(crate) struct Serie;
50
51impl Write for Serie {
52    fn write_str(&mut self, s: &str) -> core::fmt::Result {
53        for &b in s.as_bytes() {
54            serie_escribir(b);
55        }
56        Ok(())
57    }
58}
59
60/// Datos del framebuffer expuestos a los manejadores de fallo. Todo son
61/// atomicos: la baliza es intrinsecamente `Sync`, sin cerrojos.
62pub(crate) struct BalizaPanico {
63    base: AtomicPtr<u8>,
64    paso_bytes: AtomicUsize,
65    ancho: AtomicUsize,
66    alto: AtomicUsize,
67    bytes_por_pixel: AtomicUsize,
68    /// Rojo de alerta de colapso, ya codificado al formato de la pantalla.
69    pixel_alerta: AtomicU32,
70    /// Naranja de agotamiento de memoria, ya codificado.
71    pixel_oom: AtomicU32,
72}
73
74impl BalizaPanico {
75    /// Baliza apagada: sin pantalla publicada todavia.
76    const fn apagada() -> BalizaPanico {
77        BalizaPanico {
78            base: AtomicPtr::new(ptr::null_mut()),
79            paso_bytes: AtomicUsize::new(0),
80            ancho: AtomicUsize::new(0),
81            alto: AtomicUsize::new(0),
82            bytes_por_pixel: AtomicUsize::new(0),
83            pixel_alerta: AtomicU32::new(0),
84            pixel_oom: AtomicU32::new(0),
85        }
86    }
87
88    /// Enciende la baliza con los datos de una pantalla viva.
89    pub(crate) fn encender(&self, pantalla: &Pantalla, pixel_alerta: u32, pixel_oom: u32) {
90        self.paso_bytes.store(pantalla.paso_bytes, Ordering::Relaxed);
91        self.ancho.store(pantalla.ancho, Ordering::Relaxed);
92        self.alto.store(pantalla.alto, Ordering::Relaxed);
93        self.bytes_por_pixel
94            .store(pantalla.bytes_por_pixel, Ordering::Relaxed);
95        self.pixel_alerta.store(pixel_alerta, Ordering::Relaxed);
96        self.pixel_oom.store(pixel_oom, Ordering::Relaxed);
97        // La base se publica de ultima, con semantica `Release`.
98        self.base.store(pantalla.base, Ordering::Release);
99    }
100
101    /// Pinta, directa y volatilmente, una banda horizontal sobre el framebuffer
102    /// fisico. Es la herramienta de los manejadores de fallo: no confia ni en
103    /// el lienzo, ni en el heap, ni en estructura dinamica alguna.
104    fn pintar_banda(&self, y0: usize, altura: usize, pixel: u32) {
105        let base = self.base.load(Ordering::Acquire);
106        if base.is_null() {
107            return;
108        }
109        let ancho = self.ancho.load(Ordering::Relaxed);
110        let alto = self.alto.load(Ordering::Relaxed);
111        let paso = self.paso_bytes.load(Ordering::Relaxed);
112        let bpp = self.bytes_por_pixel.load(Ordering::Relaxed);
113
114        let y_fin = (y0 + altura).min(alto);
115        let mut y = y0.min(alto);
116        while y < y_fin {
117            let fila = y * paso;
118            let mut x = 0;
119            while x < ancho {
120                // SEGURIDAD: (x, y) esta acotado por las dimensiones que la
121                // baliza publico desde una pantalla real.
122                unsafe {
123                    escribir_pixel_volatil(base.add(fila + x * bpp), pixel, bpp);
124                }
125                x += 1;
126            }
127            y += 1;
128        }
129    }
130
131    /// Altura de la franja de advertencia: ~8 % de la pantalla.
132    fn franja(&self) -> usize {
133        let alto = self.alto.load(Ordering::Relaxed);
134        if alto == 0 {
135            0
136        } else {
137            (alto / 12).max(1)
138        }
139    }
140}
141
142/// Instancia global de la baliza. Comienza apagada y se enciende en el arranque.
143pub(crate) static BALIZA_PANICO: BalizaPanico = BalizaPanico::apagada();
144
145// =============================================================================
146//  MANEJADORES DE FALLO — cuando el sistema colapsa, lo DIBUJA
147// =============================================================================
148
149/// Si renaser colapsa, tatuamos una franja ROJA en lo alto del framebuffer Y
150/// dejamos por COM1 un testimonio del panico: su mensaje, su lugar — la pista
151/// que en pantalla no cabe.
152#[panic_handler]
153fn al_colapsar(info: &PanicInfo) -> ! {
154    x86_64::instructions::interrupts::disable();
155    BALIZA_PANICO.pintar_banda(
156        0,
157        BALIZA_PANICO.franja(),
158        BALIZA_PANICO.pixel_alerta.load(Ordering::Relaxed),
159    );
160    let _ = writeln!(Serie);
161    let _ = writeln!(Serie, "*** renaser :: panico ***");
162    if let Some(lugar) = info.location() {
163        let _ = writeln!(
164            Serie,
165            "  en {}:{}:{}",
166            lugar.file(),
167            lugar.line(),
168            lugar.column()
169        );
170    }
171    let _ = writeln!(Serie, "  {}", info.message());
172    crate::detener()
173}
174
175/// Si el heap se agota, tatuamos una franja NARANJA y dejamos en el serie la
176/// disposicion que reviento el techo: tamaño y alineamiento.
177#[alloc_error_handler]
178fn al_agotar_memoria(disposicion: core::alloc::Layout) -> ! {
179    x86_64::instructions::interrupts::disable();
180    BALIZA_PANICO.pintar_banda(
181        0,
182        BALIZA_PANICO.franja(),
183        BALIZA_PANICO.pixel_oom.load(Ordering::Relaxed),
184    );
185    let _ = writeln!(Serie);
186    let _ = writeln!(Serie, "*** renaser :: agotamiento de memoria ***");
187    let _ = writeln!(
188        Serie,
189        "  layout: tamaño={} alineamiento={}",
190        disposicion.size(),
191        disposicion.align()
192    );
193    crate::detener()
194}