tawasuyu

tawasuyu/brahman

Rust 521TOML 202Markdown 57JSON 197505 Definiciones

⎇ 4f31146533699ae931dbf21f680c8d06337d7b5d renaser/kernel/src/main.rs Rust · 344 líneas · 14.8 KB Historia Quién tocó qué crudo

feat(renaser): Fase 7 — apertura: plan y andamiaje del Manifiesto de Génesis 4f3114653 · 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, RegionPantalla, ALTO_MAX,
70    ANCHO_MAX,
71};
72
73/// El modulo WASM del userspace, empotrado en el binario del kernel para esta
74/// fase de pruebas. Es un `.wasm` puro, compilado aparte para `wasm32`. La
75/// Fase 5 lo instancia DOS veces —el mismo bytecode, dos regiones distintas—
76/// para demostrar la multitarea cooperativa sobre el espacio unico.
77static APP_WASM: &[u8] = include_bytes!("../assets/app.wasm");
78
79/// La aplicacion DISCOLA: un modulo WASM cuyo `tick` cae en un bucle cerrado y
80/// jamas retorna. Existe para una sola cosa — demostrar que el guardarrail de
81/// combustible la fulmina sin despeinar al kernel ni a sus vecinas.
82static DISCOLA_WASM: &[u8] = include_bytes!("../assets/discola.wasm");
83
84/// La aplicacion GLOTONA: un modulo WASM que reclama memoria lineal sin freno.
85/// Demuestra el guardarrail ESPACIAL — el techo de memoria la desaloja con la
86/// baliza amarilla, gemela de la purpura del desalojo por combustible.
87static GLOTONA_WASM: &[u8] = include_bytes!("../assets/glotona.wasm");
88
89/// La aplicacion CRONISTA: la primera ciudadana del userspace que escribe en el
90/// almacenamiento PERSISTENTE. En cada arranque graba un objeto en el grafo
91/// —enlazado al del arranque anterior—, lo corona como raiz y pinta una celda
92/// por cada arranque registrado. El disco recuerda; la cuenta sobrevive a los
93/// reinicios. Demuestra las capacidades `sys_object_*` de la Fase 6.1c.
94static CRONISTA_WASM: &[u8] = include_bytes!("../assets/cronista.wasm");
95
96/// Configuracion que el cargador `bootloader` aplicara antes de cedernos la CPU.
97static CONFIG_ARRANQUE: BootloaderConfig = {
98    let mut config = BootloaderConfig::new_default();
99    // Pedimos la memoria fisica mapeada: cimiento para futuras fases.
100    config.mappings.physical_memory = Some(Mapping::Dynamic);
101    config
102};
103
104// `entry_point!` genera el simbolo `_start`, valida la firma de `kernel_main`
105// y nos transfiere el control con seguridad de tipos.
106entry_point!(kernel_main, config = &CONFIG_ARRANQUE);
107
108/// Detiene la CPU de forma definitiva: `hlt` la duerme hasta una interrupcion.
109pub(crate) fn detener() -> ! {
110    loop {
111        x86_64::instructions::hlt();
112    }
113}
114
115/// Tarea cooperativa de una aplicacion WASM. En cada pulso del reloj le concede
116/// un `tick` —un fotograma de trabajo— y cede la CPU hasta el siguiente; entre
117/// medias corren sus vecinas. Si la app falla o agota su combustible, se la
118/// DESALOJA: se tatua su region con la baliza de purpura y la tarea concluye.
119/// El ejecutor la retira del censo, su memoria se libera, el kernel sigue vivo.
120async fn tarea_aplicacion(mut app: wasm::AplicacionWasm) {
121    loop {
122        async_system::reloj::EsperaFrame::nueva().await;
123        if let Err(falla) = app.tick() {
124            // El color de la baliza delata la causa: purpura si agoto su tiempo
125            // o aborto, amarillo si reviento su techo de memoria.
126            consola::pintar_desalojo(app.region(), falla.color_baliza());
127            return;
128        }
129    }
130}
131
132/// FASE 6.2 — la prueba viva de la E/S asincrona. Esta tarea del reactor lee el
133/// sector 0 del disco SIN bloquear: cede la CPU mientras el disco trabaja —las
134/// apps siguen pintando entre tanto— y la IRQ del disco la reanuda cuando el
135/// bloque esta listo. Deja en la consola el resultado y cuantas interrupciones
136/// de disco se atendieron — el testigo de que la E/S por sondeo quedo atras.
137async fn tarea_sonda_disco() {
138    let resultado = drivers::disco::leer_bloques(0, 1).await;
139    let Some(consola) = CONSOLA.get() else {
140        return;
141    };
142    let mut consola = consola.lock();
143    match resultado {
144        Ok(_) => consola.escribir(&format!(
145            "disco :: sonda asincrona OK -- {} IRQ de disco atendidas\n",
146            drivers::disco::pulsos_disco(),
147        )),
148        Err(motivo) => {
149            consola.escribir(&format!("disco :: sonda asincrona fallida -- {motivo}\n"))
150        }
151    }
152    consola.presentar();
153}
154
155/// Da vida a una aplicacion del userspace: la carga en su region y, si lo
156/// logra, la despacha como tarea cooperativa del reactor. Una carga fallida se
157/// salda pintando su region con la baliza de desalojo — el kernel no se inmuta.
158fn encender_app(ejecutor: &mut Executor, bytecode: &'static [u8], region: RegionPantalla) {
159    match wasm::AplicacionWasm::cargar(bytecode, region) {
160        Ok(app) => ejecutor.spawn(tarea_aplicacion(app)),
161        Err(_) => consola::pintar_desalojo(region, Color::DESALOJO),
162    }
163}
164
165/// Localiza la mayor region de RAM libre que el cargador reporto — la cantera
166/// de la que el DMA del disco tomara sus marcos fisicos.
167fn mayor_region_usable(regiones: &MemoryRegions) -> Option<(u64, u64)> {
168    regiones
169        .iter()
170        .filter(|region| matches!(region.kind, MemoryRegionKind::Usable))
171        .map(|region| (region.start, region.end))
172        .max_by_key(|&(inicio, fin)| fin - inicio)
173}
174
175/// FASE 6.1c — funda el grafo de objetos. Monta el disco virtio-blk, lee o
176/// forja el superbloque, reconstruye el indice recorriendo el log y deja
177/// constancia visual: cuantos sectores tiene el disco, cuantos objetos viven
178/// ya en el grafo y si el arranque encontro —o no— una raiz de la que tirar.
179fn informar_almacen() {
180    // Fundar el almacen ANTES de tomar el cerrojo de la consola: el montaje
181    // del disco hace E/S por sondeo y nada de ello reclama la consola.
182    let resultado = almacen::init();
183    let Some(consola) = CONSOLA.get() else {
184        return;
185    };
186    let mut consola = consola.lock();
187    match resultado {
188        Ok(resumen) => {
189            let estado = if resumen.formateado {
190                "disco formateado"
191            } else {
192                "grafo montado"
193            };
194            consola.escribir(&format!(
195                "almacen :: {} :: {} sectores :: {} objetos :: raiz {}\n",
196                estado,
197                resumen.capacidad,
198                resumen.objetos,
199                if resumen.raiz { "presente" } else { "ausente" },
200            ));
201        }
202        Err(motivo) => {
203            consola.escribir(&format!("almacen :: fallo :: {motivo}\n"));
204        }
205    }
206    // FASE 6.2 — dejar constancia de la linea de IRQ por la que el disco
207    // anunciara, ya sin sondeo, el fin de cada transferencia.
208    match drivers::disco::irq() {
209        Some(irq) => {
210            consola.escribir(&format!("disco :: virtio-blk en IRQ {irq} -- E/S asincrona\n"))
211        }
212        None => consola.escribir("disco :: IRQ no enrutada -- E/S por sondeo\n"),
213    }
214    consola.presentar();
215}
216
217// =============================================================================
218//  PUNTO DE ENTRADA DEL KERNEL
219// =============================================================================
220
221fn kernel_main(boot_info: &'static mut BootInfo) -> ! {
222    // --- 1. Recuperar el framebuffer GOP que el firmware nos confio. ---
223    let framebuffer = match boot_info.framebuffer.as_mut() {
224        Some(fb) => fb,
225        None => detener(),
226    };
227    let info: FrameBufferInfo = framebuffer.info();
228    let formato: PixelFormat = info.pixel_format;
229    let pantalla = Pantalla::adoptar(framebuffer, info);
230
231    // Datos para la sonda de disco (Fase 6.1b): el offset al que el cargador
232    // mapeo la memoria fisica y la mayor region de RAM libre para el DMA.
233    let offset_fisico = boot_info.physical_memory_offset.into_option();
234    let region_dma = mayor_region_usable(&boot_info.memory_regions);
235
236    // --- 2. Encender la baliza: la red de seguridad visual va primero. ---
237    BALIZA_PANICO.encender(
238        &pantalla,
239        codificar(formato, Color::ALERTA),
240        codificar(formato, Color::OOM),
241    );
242
243    // --- 3. Cimientos de fallos e interrupciones (Fases 2.0 y 2.1). ---
244    gdt::init();
245    interrupts::init();
246    pic::init();
247
248    // --- 4. FASE 3 :: fundar el heap. A partir de aqui, `alloc` esta vivo. ---
249    memory::init();
250
251    // --- 5. Con el heap activo, fundar lo que depende de el: el canal de
252    //        scancodes, el reloj de fotogramas y la tipografia vectorial. ---
253    async_system::teclado::init();
254    async_system::reloj::init();
255    texto::init();
256
257    // --- 6. Construir el lienzo y la consola; pintar el rotulo inicial,
258    //        ya rasterizado por fontdue, y publicar la consola globalmente. ---
259    let memoria = match reclamar_memoria_lienzo() {
260        Some(m) => m,
261        None => detener(),
262    };
263    let mut lienzo = Lienzo::nuevo(
264        memoria,
265        info.width.min(ANCHO_MAX),
266        info.height.min(ALTO_MAX),
267        formato,
268    );
269    lienzo.limpiar(Color::LIENZO_EN_REPOSO);
270
271    let mut consola = Consola::nueva(lienzo, pantalla);
272    consola.escribir("renaser :: fase 6.2 -- E/S de disco asincrona por interrupcion\n");
273    consola.presentar();
274    CONSOLA.call_once(|| Mutex::new(consola));
275
276    // --- 6.5. FASE 6.1c :: fundar el subsistema de disco y, sobre el, el grafo
277    //          de objetos: enumerar el bus PCI, montar el transporte virtio-blk,
278    //          y leer o forjar el superbloque del almacen direccionado por
279    //          contenido. El kernel adquiere, por fin, una memoria que perdura. ---
280    match (offset_fisico, region_dma) {
281        (Some(offset), Some((inicio, fin))) => {
282            drivers::disco::init(offset, inicio, fin);
283            informar_almacen();
284        }
285        _ => {
286            if let Some(consola) = CONSOLA.get() {
287                let mut consola = consola.lock();
288                consola.escribir("virtio-blk :: omitido -- memoria fisica sin mapear\n");
289                consola.presentar();
290            }
291        }
292    }
293
294    // --- 7. FASE 5/6 :: levantar el reactor y poblar el userspace con CINCO
295    //        aplicaciones WASM concurrentes, cada una en su propia region:
296    //
297    //          * App 1 — instancia de hello_wasm, a la izquierda, gobernada
298    //            por el teclado.
299    //          * App 2 — segunda instancia del MISMO bytecode, a la derecha:
300    //            un unico modulo en la RAM unificada, dos vidas aisladas.
301    //          * App discola — su `tick` es un bucle cerrado: el escudo de
302    //            COMBUSTIBLE la desaloja (baliza purpura) en su 1er fotograma.
303    //          * App glotona — reclama memoria sin freno: el escudo ESPACIAL
304    //            la desaloja (baliza amarilla) en su 1er fotograma.
305    //          * App cronista — escribe en el GRAFO DE OBJETOS persistente:
306    //            cada arranque deja un objeto enlazado al anterior y pinta una
307    //            celda por arranque. El disco recuerda entre reinicios.
308    //
309    //        Las interrupciones se habilitan AHORA: el temporizador marcara el
310    //        compas de los fotogramas y la IRQ del teclado difundira cada
311    //        scancode a los canales que las apps consultan. ---
312    let mut ejecutor = Executor::nuevo();
313    encender_app(
314        &mut ejecutor,
315        APP_WASM,
316        RegionPantalla { x: 100, y: 120, ancho: 480, alto: 560 },
317    );
318    encender_app(
319        &mut ejecutor,
320        APP_WASM,
321        RegionPantalla { x: 700, y: 120, ancho: 480, alto: 560 },
322    );
323    encender_app(
324        &mut ejecutor,
325        DISCOLA_WASM,
326        RegionPantalla { x: 60, y: 700, ancho: 360, alto: 80 },
327    );
328    encender_app(
329        &mut ejecutor,
330        GLOTONA_WASM,
331        RegionPantalla { x: 460, y: 700, ancho: 360, alto: 80 },
332    );
333    encender_app(
334        &mut ejecutor,
335        CRONISTA_WASM,
336        RegionPantalla { x: 860, y: 700, ancho: 360, alto: 80 },
337    );
338    // FASE 6.2 :: una tarea mas del reactor — no una app WASM— que sondea el
339    // disco de forma ASINCRONA: la demostracion de que la IRQ del disco
340    // conduce la E/S sin detener a las aplicaciones visuales.
341    ejecutor.spawn(tarea_sonda_disco());
342    x86_64::instructions::interrupts::enable();
343    ejecutor.run();
344}