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