tawasuyu

tawasuyu/brahman

Rust 332TOML 127JSON 17Markdown 154710 Definiciones

⎇ 42fee6fcbc7eb98267f5abfad803018bf43224a9 renaser/kernel/src/main.rs Rust · 605 líneas · 26.0 KB Historia Quién tocó qué crudo

feat(renaser): Fase 20 — Akasha Over Ether (grafo distribuido) 42fee6fcb · 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::boxed::Box;
40use alloc::format;
41
42use bootloader_api::config::{BootloaderConfig, Mapping};
43use bootloader_api::info::{FrameBufferInfo, MemoryRegionKind, MemoryRegions, PixelFormat};
44use bootloader_api::{entry_point, BootInfo};
45use spin::{Mutex, Once};
46
47// --- Subsistemas del kernel ---
48mod akasha;
49mod almacen;
50mod async_system;
51mod baliza;
52mod compositor;
53mod consola;
54mod drivers;
55mod gdt;
56mod grafico;
57mod interrupts;
58mod manifiesto;
59mod memory;
60mod pic;
61mod sync;
62mod texto;
63mod wasm;
64
65// Reexportacion para que los submodulos conserven rutas `crate::` estables.
66pub(crate) use sync::CeldaSync;
67
68use alloc::vec::Vec;
69use core::fmt::Write;
70use core::sync::atomic::{AtomicUsize, Ordering};
71
72use async_system::executor::Executor;
73use baliza::BALIZA_PANICO;
74use consola::{Consola, CONSOLA};
75use grafico::{
76    codificar, reclamar_memoria_lienzo, Color, Lienzo, Pantalla, ALTO_MAX, ANCHO_MAX,
77};
78
79/// Configuracion que el cargador `bootloader` aplicara antes de cedernos la CPU.
80static CONFIG_ARRANQUE: BootloaderConfig = {
81    let mut config = BootloaderConfig::new_default();
82    // Pedimos la memoria fisica mapeada: cimiento para futuras fases.
83    config.mappings.physical_memory = Some(Mapping::Dynamic);
84    config
85};
86
87// `entry_point!` genera el simbolo `_start`, valida la firma de `kernel_main`
88// y nos transfiere el control con seguridad de tipos.
89entry_point!(kernel_main, config = &CONFIG_ARRANQUE);
90
91/// Detiene la CPU de forma definitiva: `hlt` la duerme hasta una interrupcion.
92pub(crate) fn detener() -> ! {
93    loop {
94        x86_64::instructions::hlt();
95    }
96}
97
98/// Deja una traza por el puerto serie (COM1) — la enruta QEMU a la terminal
99/// donde se ejecuto `cargo run`. Diagnostico barato del arranque: cada hito del
100/// `kernel_main` deja una linea, asi una caida muestra HASTA DONDE llego.
101fn traza(rotulo: &str) {
102    let _ = writeln!(baliza::Serie, "boot :: {rotulo}");
103}
104
105/// FASE 10 :: el molde de una aplicacion para los lanzamientos EN VIVO. Guarda
106/// su bytecode —cacheado en RAM al arrancar, para no volver al disco despues—
107/// y la geometria y la cuota de memoria con que instanciarla.
108struct Plantilla {
109    /// Nombre legible de la app — el del manifiesto, que la barra de tareas
110    /// (Fase 14) muestra en la pestaña.
111    nombre: alloc::string::String,
112    bytecode: Vec<u8>,
113    nat_ancho: usize,
114    nat_alto: usize,
115    techo: usize,
116}
117
118/// Las plantillas de las apps de genesis. Se fijan una vez, en el arranque;
119/// cada `Alt+N` instancia la siguiente en rotacion.
120static PLANTILLAS: Once<Vec<Plantilla>> = Once::new();
121
122/// El cursor rotatorio sobre `PLANTILLAS`: que app nace en el proximo `Alt+N`.
123static CURSOR_PLANTILLA: AtomicUsize = AtomicUsize::new(0);
124
125/// Tarea cooperativa de una aplicacion WASM. En cada pulso del reloj le concede
126/// un `tick` —un fotograma de trabajo— y cede la CPU hasta el siguiente; entre
127/// medias corren sus vecinas. Si la app falla o agota su combustible, se la
128/// DESALOJA: el compositor tatua su ventana con la baliza y la tarea concluye.
129/// El ejecutor la retira del censo, su memoria se libera, el kernel sigue vivo.
130async fn tarea_aplicacion(mut app: wasm::AplicacionWasm) {
131    loop {
132        async_system::reloj::EsperaFrame::nueva().await;
133        // ¿El compositor pidio cerrar esta ventana (`Alt+Q`)? La tarea concluye
134        // por su propia voluntad: al retornar, `AplicacionWasm` se libera —su
135        // memoria lineal, su combustible, su canal de teclado— y el ejecutor la
136        // retira del censo. Una baja LIMPIA, sin baliza (Fase 10).
137        if compositor::ventana_cerrada(app.indice()) {
138            return;
139        }
140        if let Err(falla) = app.tick() {
141            // El color de la baliza delata la causa: purpura si agoto su tiempo
142            // o aborto, amarillo si reviento su techo de memoria. El compositor
143            // la pinta en el marco actual de la ventana y la marca como muerta.
144            compositor::desalojar(app.indice(), falla.color_baliza());
145            return;
146        }
147    }
148}
149
150/// FASE 8 :: la tarea del compositor. En cada fotograma drena la cola de mandos
151/// que el teclado dejo —ciclar el modo de teselado, mover el foco— y los
152/// aplica. Corre en el reactor cooperativo: el unico contexto donde es seguro
153/// re-teselar el escritorio y recomponer el lienzo desde las caches.
154async fn tarea_compositor() {
155    loop {
156        async_system::reloj::EsperaFrame::nueva().await;
157        compositor::atender_mandos();
158        // FASE 13 :: atender los eventos del raton (clic-para-enfocar y
159        // arrastre de flotantes), y refrescar el puntero si se movio en una
160        // vuelta tranquila en que ninguna app pinto.
161        compositor::atender_raton();
162        compositor::refrescar_puntero();
163        // FASE 15 :: atender la voz del kernel — pasar a la nota siguiente
164        // de la secuencia agendada, o silenciar al acabar.
165        drivers::altavoz::atender();
166        // FASE 16 :: avanzar el reloj de la barra de tareas — recompone si el
167        // segundo cambio respecto al ultimo mostrado. Si no, vuelve enseguida.
168        compositor::tick_reloj();
169        // FASE 20 :: pulso del oficio AoE — el demultiplexor de RX en cada
170        // tic (cero coste sin trafico), y el faro `AnunciarRaiz` cada 5 s
171        // (medido contra el reloj monotono, no contra los awaits).
172        akasha::tic_compositor();
173        // FASE 10 :: atender las altas en vivo. Por cada `Alt+N` pendiente,
174        // dar a luz una aplicacion nueva — el compositor solo conto la
175        // peticion; instanciar el WASM es trabajo del orquestador.
176        for _ in 0..compositor::partos_pendientes() {
177            lanzar_app();
178        }
179    }
180}
181
182/// FASE 18 — saludo inicial a la red. La tarea es CORTA y de un solo tiro:
183/// deja estabilizarse la cola RX del dispositivo y envia un ARP request al
184/// gateway de QEMU para anunciarse en capa-3. El demuxer Akasha (Fase 20)
185/// vive en el tic del compositor; el faro periodico, en `tarea_akasha_faro`.
186async fn tarea_red(mac: drivers::red::Mac) {
187    for _ in 0..10 {
188        async_system::reloj::EsperaFrame::nueva().await;
189    }
190    let frame = drivers::red::componer_arp_request(
191        mac,
192        drivers::red::IP_RENASER,
193        drivers::red::IP_GATEWAY,
194    );
195    match drivers::red::enviar(&frame) {
196        Ok(()) => {
197            let _ = writeln!(
198                baliza::Serie,
199                "red :: ARP REQUEST enviado :: ¿quien tiene 10.0.2.2?"
200            );
201        }
202        Err(motivo) => {
203            let _ = writeln!(baliza::Serie, "red :: envio fallido :: {motivo}");
204        }
205    }
206}
207
208/// FASE 20 — el faro Akasha. Cada `INTERVALO_FARO` fotogramas (= 5 s a
209/// 100 Hz) difunde por broadcast `AnunciarRaiz(manifiesto)`. La primera
210/// difusion se demora unos pocos fotogramas para que el grafo termine de
211/// montarse — si `manifiesto()` aun es `None`, el envio es no-op y el
212/// siguiente faro lo reintentara. Tarea sin fin, con la misma forma que
213/// `tarea_compositor` (probada y latiente).
214/// FASE 6.2 — la prueba viva de la E/S asincrona. Esta tarea del reactor lee el
215/// sector 0 del disco SIN bloquear: cede la CPU mientras el disco trabaja —las
216/// apps siguen pintando entre tanto— y la IRQ del disco la reanuda cuando el
217/// bloque esta listo. Deja en la consola el resultado y cuantas interrupciones
218/// de disco se atendieron — el testigo de que la E/S por sondeo quedo atras.
219async fn tarea_sonda_disco() {
220    let resultado = drivers::disco::leer_bloques(0, 1).await;
221    let Some(consola) = CONSOLA.get() else {
222        return;
223    };
224    let mut consola = consola.lock();
225    match resultado {
226        Ok(_) => consola.escribir(&format!(
227            "disco :: sonda asincrona OK -- {} IRQ de disco atendidas\n",
228            drivers::disco::pulsos_disco(),
229        )),
230        Err(motivo) => {
231            consola.escribir(&format!("disco :: sonda asincrona fallida -- {motivo}\n"))
232        }
233    }
234    consola.presentar();
235}
236
237/// Da vida a una aplicacion del userspace a partir de su `EntradaApp` del
238/// manifiesto: recupera su bytecode del grafo, lo carga en la ventana `indice`
239/// del escritorio del compositor y despacha la app como tarea cooperativa del
240/// reactor. Si el bytecode falta, esta corrupto, o la carga fracasa, el
241/// compositor desaloja esa ventana — el kernel sigue con las demas.
242fn encender_app(
243    ejecutor: &mut Executor,
244    indice: usize,
245    entrada: &manifiesto::EntradaApp,
246) -> Option<Plantilla> {
247    // El tamaño NATURAL del lienzo de la app —lo que sabe pintar, fijo— lo
248    // dicta su `EntradaApp`; el compositor decide en que marco lo coloca.
249    let natural = manifiesto::region(entrada);
250    // Recuperar el bytecode del grafo. `recuperar` recomputa el hash del
251    // objeto y verifica su integridad: un bytecode corrupto se delata aqui
252    // —y la app se niega, no se instancia un modulo en el que no se confia.
253    let bytecode = match almacen::recuperar(&entrada.bytecode) {
254        Ok(Some(objeto)) => objeto.datos,
255        _ => {
256            compositor::desalojar(indice, Color::DESALOJO);
257            return None;
258        }
259    };
260    // `indice` es la identidad de la app: su ventana en el escritorio del
261    // compositor y su ranura de estado persistido (Fase 7c).
262    match wasm::AplicacionWasm::cargar(
263        &bytecode,
264        natural.ancho,
265        natural.alto,
266        entrada.techo_memoria as usize,
267        indice,
268    ) {
269        Ok(app) => ejecutor.spawn(tarea_aplicacion(app)),
270        Err(_) => compositor::desalojar(indice, Color::DESALOJO),
271    }
272    // FASE 10 :: el bytecode, ya recuperado y verificado, queda como PLANTILLA:
273    // un molde en RAM con que `Alt+N` instanciara copias en vivo, sin volver al
274    // disco —que la E/S por sondeo en mitad del reactor seria un mal vecino—.
275    Some(Plantilla {
276        nombre: entrada.nombre.clone(),
277        bytecode,
278        nat_ancho: natural.ancho,
279        nat_alto: natural.alto,
280        techo: entrada.techo_memoria as usize,
281    })
282}
283
284/// FASE 10 :: da a luz una aplicacion EN VIVO. Elige la siguiente plantilla en
285/// rotacion, abre su ventana en el compositor —que le asigna su indice—,
286/// instancia su WASM con ese indice y engendra su tarea en el reactor ya en
287/// marcha. Si la carga falla, la ventana recien nacida se desaloja; el kernel
288/// sigue. La invoca la tarea del compositor al atender un `Alt+N`.
289fn lanzar_app() {
290    let Some(plantillas) = PLANTILLAS.get() else {
291        return;
292    };
293    if plantillas.is_empty() {
294        return;
295    }
296    // El cursor rota sobre las plantillas: cada `Alt+N` engendra la siguiente.
297    let cursor = CURSOR_PLANTILLA.fetch_add(1, Ordering::Relaxed);
298    let plantilla = &plantillas[cursor % plantillas.len()];
299
300    // La ventana nace primero: el compositor le entrega su indice —su
301    // identidad—, que el WASM necesita para hallar su ventana y su canal.
302    let indice = compositor::nacer_ventana(plantilla.nat_ancho, plantilla.nat_alto, &plantilla.nombre);
303    match wasm::AplicacionWasm::cargar(
304        &plantilla.bytecode,
305        plantilla.nat_ancho,
306        plantilla.nat_alto,
307        plantilla.techo,
308        indice,
309    ) {
310        // La tarea se ENGENDRA, no se hace `spawn`: el reactor ya corre y el
311        // ejecutor la adoptara en su proxima vuelta (Fase 10).
312        Ok(app) => async_system::executor::engendrar(Box::pin(tarea_aplicacion(app))),
313        Err(_) => compositor::desalojar(indice, Color::DESALOJO),
314    }
315}
316
317/// Escribe una linea en la consola global y la presenta. Atajo para los
318/// informes de arranque; no hace nada si la consola aun no existe.
319fn reportar(linea: &str) {
320    if let Some(consola) = CONSOLA.get() {
321        let mut consola = consola.lock();
322        consola.escribir(linea);
323        consola.escribir("\n");
324        consola.presentar();
325    }
326}
327
328/// FASE 7 :: puebla el userspace DESDE EL GRAFO. Carga el Manifiesto de
329/// Genesis que `boot` sembro en la imagen de disco, lo instala como el
330/// manifiesto VIVO del kernel y, por cada `EntradaApp`, enciende su
331/// aplicacion. Toda falla se reporta a la consola y NO detiene el arranque: el
332/// kernel se levanta con las apps que pueda — o con ninguna, si el grafo no
333/// tiene userspace.
334fn cargar_userspace(ejecutor: &mut Executor, ancho_pantalla: usize, alto_pantalla: usize) {
335    let manifiesto = match manifiesto::cargar() {
336        Ok(Some(m)) => Some(m),
337        // Disco sin manifiesto anclado: `boot` no lo sembro. El kernel se
338        // levanta sin userspace —pero se levanta—; en la practica, ninguna
339        // imagen forjada por `boot` llega aqui sin su Manifiesto de Genesis.
340        Ok(None) => {
341            reportar("manifiesto :: el disco no trae uno -- el kernel se levanta solo");
342            None
343        }
344        Err(motivo) => {
345            reportar(&format!("manifiesto :: carga fallida -- {motivo}"));
346            None
347        }
348    };
349
350    match &manifiesto {
351        Some(m) => reportar(&format!(
352            "manifiesto :: {} apps nacidas del grafo",
353            m.apps.len(),
354        )),
355        None => reportar("manifiesto :: sin userspace -- el kernel se levanta solo"),
356    }
357
358    if let Some(m) = manifiesto {
359        // Instalar el manifiesto VIVO ANTES de instanciar las apps: el `init`
360        // de cada app puede consultar su estado persistido (Fase 7c), y esa
361        // consulta lee del manifiesto vivo.
362        manifiesto::instalar(m.clone());
363
364        // FASE 8 :: fundar el escritorio del compositor — una ventana por app,
365        // con su cache de respaldo y su marco teselado por `mirada-layout`— y
366        // pintar el escenario antes de encender las apps: el teselado se ve
367        // aunque alguna app no llegue a pintar su primer fotograma.
368        let naturales: Vec<(usize, usize, &str)> = m
369            .apps
370            .iter()
371            .map(|e| (e.region_ancho as usize, e.region_alto as usize, e.nombre.as_str()))
372            .collect();
373        compositor::fundar(ancho_pantalla, alto_pantalla, &naturales);
374        compositor::componer_escenario();
375
376        let mut plantillas: Vec<Plantilla> = Vec::new();
377        for (indice, entrada) in m.apps.iter().enumerate() {
378            if let Some(plantilla) = encender_app(ejecutor, indice, entrada) {
379                plantillas.push(plantilla);
380            }
381        }
382        // FASE 10 :: fijar las plantillas de las apps. A partir de aqui, cada
383        // `Alt+N` instancia una copia viva, en rotacion.
384        PLANTILLAS.call_once(|| plantillas);
385
386        // La tarea del compositor: atiende los mandos del teclado —ciclar el
387        // teselado, mover el foco, cerrar y lanzar apps— en cada fotograma.
388        ejecutor.spawn(tarea_compositor());
389    }
390}
391
392/// Localiza la mayor region de RAM libre que el cargador reporto — la cantera
393/// de la que el DMA del disco tomara sus marcos fisicos.
394fn mayor_region_usable(regiones: &MemoryRegions) -> Option<(u64, u64)> {
395    regiones
396        .iter()
397        .filter(|region| matches!(region.kind, MemoryRegionKind::Usable))
398        .map(|region| (region.start, region.end))
399        .max_by_key(|&(inicio, fin)| fin - inicio)
400}
401
402/// FASE 6.1c — funda el grafo de objetos. Monta el disco virtio-blk, lee o
403/// forja el superbloque, reconstruye el indice recorriendo el log y deja
404/// constancia visual: cuantos sectores tiene el disco, cuantos objetos viven
405/// ya en el grafo y si el arranque encontro —o no— una raiz de la que tirar.
406fn informar_almacen() {
407    // Fundar el almacen ANTES de tomar el cerrojo de la consola: el montaje
408    // del disco hace E/S por sondeo y nada de ello reclama la consola.
409    let resultado = almacen::init();
410    let Some(consola) = CONSOLA.get() else {
411        return;
412    };
413    let mut consola = consola.lock();
414    match resultado {
415        Ok(resumen) => {
416            let estado = if resumen.formateado {
417                "disco formateado"
418            } else {
419                "grafo montado"
420            };
421            consola.escribir(&format!(
422                "almacen :: {} :: {} sectores :: {} objetos :: raiz {}\n",
423                estado,
424                resumen.capacidad,
425                resumen.objetos,
426                if resumen.raiz { "presente" } else { "ausente" },
427            ));
428        }
429        Err(motivo) => {
430            consola.escribir(&format!("almacen :: fallo :: {motivo}\n"));
431        }
432    }
433    // FASE 6.2 — dejar constancia de la linea de IRQ por la que el disco
434    // anunciara, ya sin sondeo, el fin de cada transferencia.
435    match drivers::disco::irq() {
436        Some(irq) => {
437            consola.escribir(&format!("disco :: virtio-blk en IRQ {irq} -- E/S asincrona\n"))
438        }
439        None => consola.escribir("disco :: IRQ no enrutada -- E/S por sondeo\n"),
440    }
441    consola.presentar();
442}
443
444// =============================================================================
445//  PUNTO DE ENTRADA DEL KERNEL
446// =============================================================================
447
448fn kernel_main(boot_info: &'static mut BootInfo) -> ! {
449    traza("kernel_main entrado");
450    // --- 1. Recuperar el framebuffer GOP que el firmware nos confio. ---
451    let framebuffer = match boot_info.framebuffer.as_mut() {
452        Some(fb) => fb,
453        None => detener(),
454    };
455    let info: FrameBufferInfo = framebuffer.info();
456    let formato: PixelFormat = info.pixel_format;
457    let pantalla = Pantalla::adoptar(framebuffer, info);
458    traza("framebuffer adoptado");
459
460    // Datos para la sonda de disco (Fase 6.1b): el offset al que el cargador
461    // mapeo la memoria fisica y la mayor region de RAM libre para el DMA.
462    let offset_fisico = boot_info.physical_memory_offset.into_option();
463    let region_dma = mayor_region_usable(&boot_info.memory_regions);
464    let _ = writeln!(
465        baliza::Serie,
466        "boot :: physical_memory_offset={:#x?} region_dma={:#x?}",
467        offset_fisico,
468        region_dma,
469    );
470
471    // --- 2. Encender la baliza: la red de seguridad visual va primero. ---
472    BALIZA_PANICO.encender(
473        &pantalla,
474        codificar(formato, Color::ALERTA),
475        codificar(formato, Color::OOM),
476    );
477    traza("baliza encendida");
478
479    // --- 3. Cimientos de fallos e interrupciones (Fases 2.0 y 2.1). ---
480    gdt::init();
481    interrupts::init();
482    pic::init();
483    traza("gdt + idt + pic");
484
485    // --- 4. FASE 3 :: fundar el heap. A partir de aqui, `alloc` esta vivo. ---
486    memory::init();
487    traza("heap fundado");
488
489    // --- 4.5. Mapeador de MMIO: envuelve la tabla L4 activa para abrir paginas
490    //          nuevas hacia los BAR MMIO de virtio (que pueden caer fuera de
491    //          lo que el cargador mapeo). Necesita `physical_memory_offset`
492    //          para alcanzar la L4 via el mapeo de memoria fisica. ---
493    if let Some(offset) = offset_fisico {
494        memory::mmio::init(offset);
495        traza("mmio :: mapeador fundado");
496    }
497
498    // --- 5. Con el heap activo, fundar lo que depende de el: el canal de
499    //        scancodes, el reloj de fotogramas y la tipografia vectorial. ---
500    async_system::teclado::init();
501    async_system::reloj::init();
502    texto::init();
503    traza("teclado + reloj + texto");
504
505    // --- 6. Construir el lienzo y la consola; pintar el rotulo inicial,
506    //        ya rasterizado por fontdue, y publicar la consola globalmente. ---
507    let memoria = match reclamar_memoria_lienzo() {
508        Some(m) => m,
509        None => detener(),
510    };
511    let ancho_lienzo = info.width.min(ANCHO_MAX);
512    let alto_lienzo = info.height.min(ALTO_MAX);
513    let mut lienzo = Lienzo::nuevo(memoria, ancho_lienzo, alto_lienzo, formato);
514    lienzo.limpiar(Color::LIENZO_EN_REPOSO);
515
516    let mut consola = Consola::nueva(lienzo, pantalla);
517    consola.escribir("renaser :: fase 6.2 -- E/S de disco asincrona por interrupcion\n");
518    consola.presentar();
519    CONSOLA.call_once(|| Mutex::new(consola));
520    traza("consola publicada");
521
522    // --- 6.5. FASE 6.1c :: fundar el subsistema de disco y, sobre el, el grafo
523    //          de objetos: enumerar el bus PCI, montar el transporte virtio-blk,
524    //          y leer o forjar el superbloque del almacen direccionado por
525    //          contenido. El kernel adquiere, por fin, una memoria que perdura. ---
526    match (offset_fisico, region_dma) {
527        (Some(offset), Some((inicio, fin))) => {
528            traza("disco :: init");
529            drivers::disco::init(offset, inicio, fin);
530            traza("almacen :: init");
531            informar_almacen();
532            traza("almacen :: listo");
533        }
534        _ => {
535            if let Some(consola) = CONSOLA.get() {
536                let mut consola = consola.lock();
537                consola.escribir("virtio-blk :: omitido -- memoria fisica sin mapear\n");
538                consola.presentar();
539            }
540            traza("disco :: OMITIDO (sin offset/region)");
541        }
542    }
543
544    // --- 6.6. FASE 13 :: despertar el raton PS/2. El 8042 enciende su
545    //          dispositivo auxiliar, el raton empieza a reportar, y el PIC
546    //          desenmascara su IRQ12. Desde aqui hay un puntero en pantalla,
547    //          y los clics pueden alcanzar al compositor.
548    drivers::raton::init(ancho_lienzo, alto_lienzo);
549    traza("raton :: listo");
550
551    // --- 6.7. FASE 18 :: montar la tarjeta virtio-net. Si el firmware no
552    //          enruta una linea de IRQ util o no hay dispositivo, el resto
553    //          del arranque sigue — la red NO es critica.
554    let mac_red = drivers::red::montar();
555    match mac_red {
556        Ok(mac) => {
557            let _ = writeln!(
558                baliza::Serie,
559                "red :: virtio-net :: MAC {:02x}:{:02x}:{:02x}:{:02x}:{:02x}:{:02x} :: IRQ {:?}",
560                mac[0], mac[1], mac[2], mac[3], mac[4], mac[5],
561                drivers::red::irq(),
562            );
563            // FASE 20 :: registrar la MAC en el respondedor Akasha, para que
564            // pueda firmar sus frames AoE.
565            akasha::montar(mac);
566        }
567        Err(motivo) => {
568            let _ = writeln!(baliza::Serie, "red :: virtio-net :: {motivo}");
569        }
570    }
571    traza("red :: listo");
572
573    // --- 7. FASE 7 :: levantar el reactor y poblar el userspace DESDE EL
574    //        GRAFO. El kernel ya no empotra los modulos WASM: lee el
575    //        Manifiesto de Genesis que `boot` sembro en la imagen de disco e
576    //        instancia cada `EntradaApp` recuperando su bytecode del grafo de
577    //        objetos. Las cinco apps de genesis (dos instancias de hello, la
578    //        discola, la glotona y la cronista) nacen del disco, no del
579    //        binario del kernel.
580    //
581    //        Las interrupciones se habilitan AHORA: el temporizador marcara el
582    //        compas de los fotogramas y la IRQ del teclado difundira cada
583    //        scancode a los canales que las apps consultan. ---
584    let mut ejecutor = Executor::nuevo();
585    traza("ejecutor :: creado");
586    cargar_userspace(&mut ejecutor, ancho_lienzo, alto_lienzo);
587    traza("userspace :: cargado");
588    // FASE 6.2 :: una tarea mas del reactor — no una app WASM— que sondea el
589    // disco de forma ASINCRONA: la demostracion de que la IRQ del disco
590    // conduce la E/S sin detener a las aplicaciones visuales.
591    ejecutor.spawn(tarea_sonda_disco());
592    // FASE 18 :: si la tarjeta de red se monto, una tarea corta envia un ARP
593    // al gateway para anunciarse en capa-3. El oficio AoE (Fase 20) — demuxer
594    // de entrada + faro periodico — va clavado al tic del compositor.
595    if let Ok(mac) = mac_red {
596        ejecutor.spawn(tarea_red(mac));
597    }
598    // FASE 15 :: la voz del sistema da los buenos dias con un acorde de Do
599    // mayor. La tarea del compositor lo hara sonar nota a nota una vez que
600    // el reactor arranque y las interrupciones empiecen a llegar.
601    drivers::altavoz::agendar(&drivers::altavoz::VOZ_BIENVENIDA);
602    traza("ejecutor :: arrancando reactor");
603    x86_64::instructions::interrupts::enable();
604    ejecutor.run();
605}