gioser

tawasuyu/brahman

Rust 326TOML 123JSON 17Markdown 144606 Definiciones

⎇ 8f946b449c2e6dc4f2bf09921750075499193a05 renaser/kernel/src/main.rs Rust · 393 líneas · 16.7 KB Historia Quién tocó qué crudo

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