⎇ 89117f47cc75246d5e14152b97974366c85c19e9
renaser/kernel/src/baliza.rs
History
Who touched what
raw
diag(renaser): trazado por serie del arranque + DMA salta la página 0
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}