gioser

tawasuyu/brahman

Rust 334TOML 129Markdown 39JSON 174745 Definitions

⎇ 900cd19e496f1e45eab2823e909ace7625f78f3a renaser/kernel/src/main.rs Rust · 357 lines · 15.2 KB History Who touched what raw

feat(renaser): Fase 7c — persistencia inter-sesión por-app 900cd19e4 · sergio ·

1// =============================================================================
2//  renaser :: kernel/src/main.rs — el punto de entrada y la orquestacion
3// -----------------------------------------------------------------------------
4//  Aqui no nace una terminal: nace una superficie. renaser es un kernel
5//  asincrono de Espacio de Direccionamiento Unico (SASOS) que rompe con el
6//  paradigma POSIX — sin TTYs, sin archivos planos, sin capas GNU.
7//
8//  Este archivo es deliberadamente delgado: solo el arranque y el cableado.
9//  Cada subsistema vive en su propio modulo (ver `ARCHITECTURE.md`):
10//
11//    grafico      — color, framebuffer fisico y lienzo de doble bufer.
12//    consola      — superficie de texto/imagen; rasteriza glifos con fontdue.
13//    baliza       — red de seguridad visual; manejadores de panico y de OOM.
14//    sync         — `CeldaSync`, la celda de inicializacion unica.
15//    gdt          — GDT + TSS + stack de emergencia del doble fallo.
16//    interrupts   — IDT, excepciones de CPU e interrupciones de hardware.
17//    pic          — el par 8259 remapeado y el temporizador (PIT).
18//    drivers      — descubrimiento de hardware y E/S de disco asincrona por
19//                   interrupcion: el bus PCI y el virtio-blk (Fases 6.1, 6.2).
20//    almacen      — el grafo de objetos direccionado por contenido (Fase 6.1c).
21//    manifiesto   — el Manifiesto de Genesis: que apps nacen del grafo (Fase 7).
22//    memory       — el heap dinamico del kernel (`#[global_allocator]`).
23//    async_system — el reactor cooperativo: ejecutor, tareas, wakers, teclado
24//                   y el reloj que marca el compas de los fotogramas (Fase 5).
25//    texto        — rasterizacion de tipografia vectorial (fontdue).
26//    wasm         — el runtime WebAssembly, la matriz de capacidades y el
27//                   escudo de combustible que acota el tiempo de cada app.
28// =============================================================================
29
30#![no_std] // Prohibido `std`: bajo nosotros no hay sistema operativo alguno.
31#![no_main] // El punto de entrada lo define el cargador, no la convencion C.
32#![feature(abi_x86_interrupt)] // ABI de las rutinas de excepcion (Fase 2).
33#![feature(alloc_error_handler)] // Manejador propio de agotamiento de heap (Fase 3).
34#![deny(unsafe_op_in_unsafe_fn)] // Cada operacion `unsafe` se justifica explicitamente.
35
36extern crate alloc; // El heap esta vivo: `alloc::*` queda disponible (Fase 3).
37
38use alloc::format;
39
40use bootloader_api::config::{BootloaderConfig, Mapping};
41use bootloader_api::info::{FrameBufferInfo, MemoryRegionKind, MemoryRegions, PixelFormat};
42use bootloader_api::{entry_point, BootInfo};
43use spin::Mutex;
44
45// --- Subsistemas del kernel ---
46mod almacen;
47mod async_system;
48mod baliza;
49mod consola;
50mod drivers;
51mod gdt;
52mod grafico;
53mod interrupts;
54mod manifiesto;
55mod memory;
56mod pic;
57mod sync;
58mod texto;
59mod wasm;
60
61// Reexportaciones para que los submodulos conserven rutas `crate::` estables.
62pub(crate) use consola::volcar_marco_wasm;
63pub(crate) use sync::CeldaSync;
64
65use async_system::executor::Executor;
66use baliza::BALIZA_PANICO;
67use consola::{Consola, CONSOLA};
68use grafico::{
69    codificar, reclamar_memoria_lienzo, Color, Lienzo, Pantalla, ALTO_MAX, ANCHO_MAX,
70};
71
72/// Configuracion que el cargador `bootloader` aplicara antes de cedernos la CPU.
73static CONFIG_ARRANQUE: BootloaderConfig = {
74    let mut config = BootloaderConfig::new_default();
75    // Pedimos la memoria fisica mapeada: cimiento para futuras fases.
76    config.mappings.physical_memory = Some(Mapping::Dynamic);
77    config
78};
79
80// `entry_point!` genera el simbolo `_start`, valida la firma de `kernel_main`
81// y nos transfiere el control con seguridad de tipos.
82entry_point!(kernel_main, config = &CONFIG_ARRANQUE);
83
84/// Detiene la CPU de forma definitiva: `hlt` la duerme hasta una interrupcion.
85pub(crate) fn detener() -> ! {
86    loop {
87        x86_64::instructions::hlt();
88    }
89}
90
91/// Tarea cooperativa de una aplicacion WASM. En cada pulso del reloj le concede
92/// un `tick` —un fotograma de trabajo— y cede la CPU hasta el siguiente; entre
93/// medias corren sus vecinas. Si la app falla o agota su combustible, se la
94/// DESALOJA: se tatua su region con la baliza de purpura y la tarea concluye.
95/// El ejecutor la retira del censo, su memoria se libera, el kernel sigue vivo.
96async fn tarea_aplicacion(mut app: wasm::AplicacionWasm) {
97    loop {
98        async_system::reloj::EsperaFrame::nueva().await;
99        if let Err(falla) = app.tick() {
100            // El color de la baliza delata la causa: purpura si agoto su tiempo
101            // o aborto, amarillo si reviento su techo de memoria.
102            consola::pintar_desalojo(app.region(), falla.color_baliza());
103            return;
104        }
105    }
106}
107
108/// FASE 6.2 — la prueba viva de la E/S asincrona. Esta tarea del reactor lee el
109/// sector 0 del disco SIN bloquear: cede la CPU mientras el disco trabaja —las
110/// apps siguen pintando entre tanto— y la IRQ del disco la reanuda cuando el
111/// bloque esta listo. Deja en la consola el resultado y cuantas interrupciones
112/// de disco se atendieron — el testigo de que la E/S por sondeo quedo atras.
113async fn tarea_sonda_disco() {
114    let resultado = drivers::disco::leer_bloques(0, 1).await;
115    let Some(consola) = CONSOLA.get() else {
116        return;
117    };
118    let mut consola = consola.lock();
119    match resultado {
120        Ok(_) => consola.escribir(&format!(
121            "disco :: sonda asincrona OK -- {} IRQ de disco atendidas\n",
122            drivers::disco::pulsos_disco(),
123        )),
124        Err(motivo) => {
125            consola.escribir(&format!("disco :: sonda asincrona fallida -- {motivo}\n"))
126        }
127    }
128    consola.presentar();
129}
130
131/// Da vida a una aplicacion del userspace a partir de su `EntradaApp` del
132/// manifiesto: recupera su bytecode del grafo, la carga en su region y la
133/// despacha como tarea cooperativa del reactor. Si el bytecode falta, esta
134/// corrupto, o la carga fracasa, se salda pintando la region de la app con
135/// la baliza de desalojo — el kernel no se inmuta y sigue con las demas.
136fn encender_app(ejecutor: &mut Executor, indice: usize, entrada: &manifiesto::EntradaApp) {
137    let region = manifiesto::region(entrada);
138    // Recuperar el bytecode del grafo. `recuperar` recomputa el hash del
139    // objeto y verifica su integridad: un bytecode corrupto se delata aqui
140    // —y la app se niega, no se instancia un modulo en el que no se confia.
141    let bytecode = match almacen::recuperar(&entrada.bytecode) {
142        Ok(Some(objeto)) => objeto.datos,
143        _ => {
144            consola::pintar_desalojo(region, Color::DESALOJO);
145            return;
146        }
147    };
148    // `indice` es la identidad de la app en el manifiesto: las capacidades de
149    // estado persistido (Fase 7c) la usan para hallar SU ranura `estado`.
150    match wasm::AplicacionWasm::cargar(&bytecode, region, entrada.techo_memoria as usize, indice) {
151        Ok(app) => ejecutor.spawn(tarea_aplicacion(app)),
152        Err(_) => consola::pintar_desalojo(region, Color::DESALOJO),
153    }
154}
155
156/// Escribe una linea en la consola global y la presenta. Atajo para los
157/// informes de arranque; no hace nada si la consola aun no existe.
158fn reportar(linea: &str) {
159    if let Some(consola) = CONSOLA.get() {
160        let mut consola = consola.lock();
161        consola.escribir(linea);
162        consola.escribir("\n");
163        consola.presentar();
164    }
165}
166
167/// FASE 7 :: puebla el userspace DESDE EL GRAFO. Carga el Manifiesto de
168/// Genesis que `boot` sembro en la imagen de disco, lo instala como el
169/// manifiesto VIVO del kernel y, por cada `EntradaApp`, enciende su
170/// aplicacion. Toda falla se reporta a la consola y NO detiene el arranque: el
171/// kernel se levanta con las apps que pueda — o con ninguna, si el grafo no
172/// tiene userspace.
173fn cargar_userspace(ejecutor: &mut Executor) {
174    let manifiesto = match manifiesto::cargar() {
175        Ok(Some(m)) => Some(m),
176        // Disco sin manifiesto anclado: `boot` no lo sembro. El kernel se
177        // levanta sin userspace —pero se levanta—; en la practica, ninguna
178        // imagen forjada por `boot` llega aqui sin su Manifiesto de Genesis.
179        Ok(None) => {
180            reportar("manifiesto :: el disco no trae uno -- el kernel se levanta solo");
181            None
182        }
183        Err(motivo) => {
184            reportar(&format!("manifiesto :: carga fallida -- {motivo}"));
185            None
186        }
187    };
188
189    match &manifiesto {
190        Some(m) => reportar(&format!(
191            "manifiesto :: {} apps nacidas del grafo",
192            m.apps.len(),
193        )),
194        None => reportar("manifiesto :: sin userspace -- el kernel se levanta solo"),
195    }
196
197    if let Some(m) = manifiesto {
198        // Instalar el manifiesto VIVO ANTES de instanciar las apps: el `init`
199        // de cada app puede consultar su estado persistido (Fase 7c), y esa
200        // consulta lee del manifiesto vivo. Se instala una copia; la otra se
201        // itera para encender cada app con su indice — su identidad.
202        manifiesto::instalar(m.clone());
203        for (indice, entrada) in m.apps.iter().enumerate() {
204            encender_app(ejecutor, indice, entrada);
205        }
206    }
207}
208
209/// Localiza la mayor region de RAM libre que el cargador reporto — la cantera
210/// de la que el DMA del disco tomara sus marcos fisicos.
211fn mayor_region_usable(regiones: &MemoryRegions) -> Option<(u64, u64)> {
212    regiones
213        .iter()
214        .filter(|region| matches!(region.kind, MemoryRegionKind::Usable))
215        .map(|region| (region.start, region.end))
216        .max_by_key(|&(inicio, fin)| fin - inicio)
217}
218
219/// FASE 6.1c — funda el grafo de objetos. Monta el disco virtio-blk, lee o
220/// forja el superbloque, reconstruye el indice recorriendo el log y deja
221/// constancia visual: cuantos sectores tiene el disco, cuantos objetos viven
222/// ya en el grafo y si el arranque encontro —o no— una raiz de la que tirar.
223fn informar_almacen() {
224    // Fundar el almacen ANTES de tomar el cerrojo de la consola: el montaje
225    // del disco hace E/S por sondeo y nada de ello reclama la consola.
226    let resultado = almacen::init();
227    let Some(consola) = CONSOLA.get() else {
228        return;
229    };
230    let mut consola = consola.lock();
231    match resultado {
232        Ok(resumen) => {
233            let estado = if resumen.formateado {
234                "disco formateado"
235            } else {
236                "grafo montado"
237            };
238            consola.escribir(&format!(
239                "almacen :: {} :: {} sectores :: {} objetos :: raiz {}\n",
240                estado,
241                resumen.capacidad,
242                resumen.objetos,
243                if resumen.raiz { "presente" } else { "ausente" },
244            ));
245        }
246        Err(motivo) => {
247            consola.escribir(&format!("almacen :: fallo :: {motivo}\n"));
248        }
249    }
250    // FASE 6.2 — dejar constancia de la linea de IRQ por la que el disco
251    // anunciara, ya sin sondeo, el fin de cada transferencia.
252    match drivers::disco::irq() {
253        Some(irq) => {
254            consola.escribir(&format!("disco :: virtio-blk en IRQ {irq} -- E/S asincrona\n"))
255        }
256        None => consola.escribir("disco :: IRQ no enrutada -- E/S por sondeo\n"),
257    }
258    consola.presentar();
259}
260
261// =============================================================================
262//  PUNTO DE ENTRADA DEL KERNEL
263// =============================================================================
264
265fn kernel_main(boot_info: &'static mut BootInfo) -> ! {
266    // --- 1. Recuperar el framebuffer GOP que el firmware nos confio. ---
267    let framebuffer = match boot_info.framebuffer.as_mut() {
268        Some(fb) => fb,
269        None => detener(),
270    };
271    let info: FrameBufferInfo = framebuffer.info();
272    let formato: PixelFormat = info.pixel_format;
273    let pantalla = Pantalla::adoptar(framebuffer, info);
274
275    // Datos para la sonda de disco (Fase 6.1b): el offset al que el cargador
276    // mapeo la memoria fisica y la mayor region de RAM libre para el DMA.
277    let offset_fisico = boot_info.physical_memory_offset.into_option();
278    let region_dma = mayor_region_usable(&boot_info.memory_regions);
279
280    // --- 2. Encender la baliza: la red de seguridad visual va primero. ---
281    BALIZA_PANICO.encender(
282        &pantalla,
283        codificar(formato, Color::ALERTA),
284        codificar(formato, Color::OOM),
285    );
286
287    // --- 3. Cimientos de fallos e interrupciones (Fases 2.0 y 2.1). ---
288    gdt::init();
289    interrupts::init();
290    pic::init();
291
292    // --- 4. FASE 3 :: fundar el heap. A partir de aqui, `alloc` esta vivo. ---
293    memory::init();
294
295    // --- 5. Con el heap activo, fundar lo que depende de el: el canal de
296    //        scancodes, el reloj de fotogramas y la tipografia vectorial. ---
297    async_system::teclado::init();
298    async_system::reloj::init();
299    texto::init();
300
301    // --- 6. Construir el lienzo y la consola; pintar el rotulo inicial,
302    //        ya rasterizado por fontdue, y publicar la consola globalmente. ---
303    let memoria = match reclamar_memoria_lienzo() {
304        Some(m) => m,
305        None => detener(),
306    };
307    let mut lienzo = Lienzo::nuevo(
308        memoria,
309        info.width.min(ANCHO_MAX),
310        info.height.min(ALTO_MAX),
311        formato,
312    );
313    lienzo.limpiar(Color::LIENZO_EN_REPOSO);
314
315    let mut consola = Consola::nueva(lienzo, pantalla);
316    consola.escribir("renaser :: fase 6.2 -- E/S de disco asincrona por interrupcion\n");
317    consola.presentar();
318    CONSOLA.call_once(|| Mutex::new(consola));
319
320    // --- 6.5. FASE 6.1c :: fundar el subsistema de disco y, sobre el, el grafo
321    //          de objetos: enumerar el bus PCI, montar el transporte virtio-blk,
322    //          y leer o forjar el superbloque del almacen direccionado por
323    //          contenido. El kernel adquiere, por fin, una memoria que perdura. ---
324    match (offset_fisico, region_dma) {
325        (Some(offset), Some((inicio, fin))) => {
326            drivers::disco::init(offset, inicio, fin);
327            informar_almacen();
328        }
329        _ => {
330            if let Some(consola) = CONSOLA.get() {
331                let mut consola = consola.lock();
332                consola.escribir("virtio-blk :: omitido -- memoria fisica sin mapear\n");
333                consola.presentar();
334            }
335        }
336    }
337
338    // --- 7. FASE 7 :: levantar el reactor y poblar el userspace DESDE EL
339    //        GRAFO. El kernel ya no empotra los modulos WASM: lee el
340    //        Manifiesto de Genesis que `boot` sembro en la imagen de disco e
341    //        instancia cada `EntradaApp` recuperando su bytecode del grafo de
342    //        objetos. Las cinco apps de genesis (dos instancias de hello, la
343    //        discola, la glotona y la cronista) nacen del disco, no del
344    //        binario del kernel.
345    //
346    //        Las interrupciones se habilitan AHORA: el temporizador marcara el
347    //        compas de los fotogramas y la IRQ del teclado difundira cada
348    //        scancode a los canales que las apps consultan. ---
349    let mut ejecutor = Executor::nuevo();
350    cargar_userspace(&mut ejecutor);
351    // FASE 6.2 :: una tarea mas del reactor — no una app WASM— que sondea el
352    // disco de forma ASINCRONA: la demostracion de que la IRQ del disco
353    // conduce la E/S sin detener a las aplicaciones visuales.
354    ejecutor.spawn(tarea_sonda_disco());
355    x86_64::instructions::interrupts::enable();
356    ejecutor.run();
357}