diff --git a/renaser/CHANGELOG.md b/renaser/CHANGELOG.md index db1b321..0de6bcc 100644 --- a/renaser/CHANGELOG.md +++ b/renaser/CHANGELOG.md @@ -455,3 +455,28 @@ REACTIVA, guiada por la interrupción física del dispositivo. testigo de integridad del DAG en verde — la nueva E/S asíncrona lee y escribe el grafo sin un solo fallo. Las cuatro apps WASM siguen su curso, sin un sobresalto ni un micro-congelamiento. + +## Fase 7 — Apertura: Apps que nacen del Grafo — 2026-05-22 + +Apertura de la Fase 7. Este commit asienta el plan y el andamiaje; **no +cambia el comportamiento del kernel**. La Fase 7 destierra el +`include_bytes!` del userspace: las aplicaciones pasan a ser objetos del +grafo, y un Manifiesto de Génesis —también en el grafo— dicta qué arranca, +con qué cuota y en qué región. + +### Añadido +- `FASE7.md` — el plan de ataque: el objetivo, el problema huevo-y-gallina + de la génesis, las sub-fases 7a (manifiesto + carga desde el grafo, con + semilla por el kernel), 7b (siembra de la imagen por `boot`, muerte del + `include_bytes!`) y 7c (persistencia inter-sesión), y los guardarraíles. +- `kernel/src/manifiesto.rs` — andamiaje del Manifiesto de Génesis. Los + tipos `Manifiesto` / `EntradaApp` y la (de)serialización `postcard` están + completos; `cargar` y `sembrar_genesis` son esbozos hasta la Fase 7a. + La sub-región se guarda en campos `u32` de ancho fijo (formato en disco), + no como `RegionPantalla` (`usize`, ancho de plataforma). + +### Notas +- El módulo se declara en `main.rs` pero aún no se cablea a `kernel_main` + (`#![allow(dead_code)]` temporal, hasta que la 7a le dé un llamador). El + kernel compila y se comporta idéntico a la Fase 6.2 — nada observable que + verificar en QEMU en esta apertura. diff --git a/renaser/DIARIO.md b/renaser/DIARIO.md index 64bb2cc..e183f79 100644 --- a/renaser/DIARIO.md +++ b/renaser/DIARIO.md @@ -221,6 +221,24 @@ casilla más cada vez— pero que se sentirá en todo lo que venga después. El y renaser ya no se miran fijamente: se hablan, y entre frase y frase, cada cual atiende lo suyo. +## El plano antes de la obra — abrir la Fase 7 + +Hay jornadas en las que no se levanta un muro: se dibuja. Esta fue una de +ésas. Antes de tocar el corazón del kernel hubo que decidir, con calma, +hacia dónde mover la siguiente piedra. + +La pregunta era vieja y conocida: las aplicaciones de renaser todavía viajan +escondidas dentro del propio kernel, cosidas a su binario. Pero renaser tiene +un disco que recuerda, un tejido de objetos que perdura entre apagones. ¿Por +qué, entonces, las apps no nacen de ahí, como todo lo demás? + +Se escribió el plano de esa mudanza —el plan de la Fase 7— y se dejó puesta +la primera viga: un cuaderno nuevo, el «Manifiesto», donde algún día estará +escrito qué programas deben despertar, cuánta memoria se les presta y en qué +rincón de la pantalla viven. Por ahora el cuaderno está en blanco y sus +páginas son sólo molde; pero el molde ya tiene la forma exacta de lo que +vendrá. La casa no cambió aún — sólo sabe, por fin, hacia dónde va a crecer. + --- *El diario continúa. La próxima página la escribirá la próxima jornada.* diff --git a/renaser/FASE7.md b/renaser/FASE7.md new file mode 100644 index 0000000..1379c0d --- /dev/null +++ b/renaser/FASE7.md @@ -0,0 +1,97 @@ +# renaser — Fase 7 :: Apps que nacen del Grafo + +Plan de ataque. Para el estado general ver `ROADMAP.md`; para la arquitectura, +`ARCHITECTURE.md`. + +## El objetivo + +Hoy el kernel lleva el userspace **empotrado en su propio binario**: +`main.rs` tiene cuatro `include_bytes!` (`app.wasm`, `discola.wasm`, +`glotona.wasm`, `cronista.wasm`) y cinco llamadas `encender_app` con regiones +escritas a mano. Eso contradice la tesis de renaser: el almacenamiento es un +**grafo de objetos direccionado por contenido**, no un binario monolítico. + +La Fase 7 **destruye el `include_bytes!` del userspace**. Las aplicaciones +pasan a ser objetos del grafo; el kernel las descubre, las verifica por su +hash y las inyecta en caliente en `wasmi`. Qué apps arrancan, con qué cuota y +en qué región lo dicta un **Manifiesto de Génesis** que también vive en el +grafo. (La fuente `font.ttf` sigue empotrada: es del kernel, no del userspace.) + +## El problema de la génesis + +Hay un huevo-y-gallina: el kernel lee el manifiesto para saber qué cargar, +pero el manifiesto y el bytecode tienen que haber sido escritos antes. En un +disco virgen no hay nada. Dos vías para sembrarlo: + +- **A — el kernel siembra.** En un disco sin manifiesto, el kernel graba en el + grafo el bytecode de unas apps de génesis y un manifiesto por defecto. El + bytecode de génesis llega vía `include_bytes!` — todavía. +- **B — `boot` siembra la imagen.** El constructor de imagen (anfitrión) + pre-puebla el disco con los objetos de bytecode y el manifiesto. El kernel + jamás empotra una app. + +B es el estado final puro, pero exige enseñarle a `boot` el formato del grafo +(objetos, BLAKE3, log, superbloque) — trabajo de anfitrión considerable. Se +ataca **incremental**, como las Fases 6.1a/b/c. + +## Sub-fases + +### 7a — El Manifiesto y la carga desde el grafo (semilla por el kernel) + +1. **`manifiesto.rs`** *(andamiaje ya creado)* — tipos `Manifiesto` / + `EntradaApp`, (de)serialización postcard. Hoy es un módulo `no_std` del + kernel; no se comparte con las apps (la frontera app↔kernel es el ABI WASM + numérico, no tipos Rust). +2. **`almacen.rs`** — el `SuperBloque` gana un segundo ancla, + `manifiesto: Option`, gemelo de `raiz`. `VERSION` sube de `1` a `2` + (un disco v1 se reformatea, como ya hace `init` con discos ajenos). + Getters `manifiesto()` / `fijar_manifiesto()`. +3. **`manifiesto::sembrar_genesis()`** — en un disco sin manifiesto: graba el + bytecode de las apps de génesis (`include_bytes!`, **sólo aquí**), compone + un `Manifiesto` por defecto con sus regiones y cuotas, lo graba y lo ancla. +4. **`manifiesto::cargar()`** — lee el ancla del superbloque, recupera el + objeto, lo deserializa. +5. **`kernel_main`** — reemplaza las cinco `encender_app` escritas a mano por: + cargar el manifiesto (o sembrarlo si falta) e iterar sus `EntradaApp`. +6. **`wasm/mod.rs`** — el techo de memoria deja de ser la constante + `TECHO_MEMORIA` y pasa a venir por-app del manifiesto (`EntradaApp`). +7. **Verificar en QEMU** — la pantalla debe verse idéntica a la Fase 6.2 + (cinco apps en sus regiones), pero ahora **nacidas del grafo**. + +### 7b — La imagen sembrada por `boot`; muere `include_bytes!` + +- `boot` (anfitrión) aprende el formato del grafo y pre-puebla la imagen de + disco con los objetos de bytecode + el manifiesto. +- El kernel pierde los `include_bytes!` del userspace; `sembrar_genesis` se + retira o queda como camino vacío de respaldo. + +### 7c — Persistencia inter-sesión + +- Una app (cronista evolucionada, o una nueva interactiva) guarda su estado + mutado como un objeto nuevo del grafo al recibir teclado. +- Cada `EntradaApp` usa su campo `estado: Option` (ya previsto en el + tipo). Al guardar, el kernel reescribe el manifiesto; al arrancar, le pasa + ese estado a la app para que despierte donde quedó. +- Probablemente una capacidad host nueva para que el kernel se entere del + hash de estado nuevo que la app produjo. + +## Guardarraíles (directiva de la arquitecta) + +- **No romper `check-shared-cores.sh`.** Si algún tipo del manifiesto llegara + a compartirse entre apps y kernel, debe habitar un núcleo estrictamente + `no_std`. Hoy no se comparte: `manifiesto.rs` es kernel-only. +- **Errores recuperables con `Result`.** Si el kernel lee del grafo un objeto + de bytecode corrupto (su hash recomputado ≠ su id), `almacen::recuperar` ya + lo detecta — devuelve `Err`. El kernel **niega esa instanciación**, pinta la + baliza de desalojo en la región de esa app, y **sigue levantando el resto**. + Un módulo podrido nunca tumba el arranque. + +## Estructura de archivos + +| archivo | estado | rol en la Fase 7 | +| --- | --- | --- | +| `kernel/src/manifiesto.rs` | **nuevo (7a)** | tipos `Manifiesto`/`EntradaApp` + (de)serialización + carga/siembra | +| `kernel/src/almacen.rs` | a modificar (7a) | `SuperBloque.manifiesto`, `VERSION` 2, getters | +| `kernel/src/main.rs` | a modificar (7a) | `kernel_main` itera el manifiesto en vez de `include_bytes!` | +| `kernel/src/wasm/mod.rs` | a modificar (7a) | techo de memoria por-app desde el manifiesto | +| `boot/` | a modificar (7b) | siembra la imagen de disco con el grafo | diff --git a/renaser/kernel/src/main.rs b/renaser/kernel/src/main.rs index 1c24ec9..2639674 100644 --- a/renaser/kernel/src/main.rs +++ b/renaser/kernel/src/main.rs @@ -18,6 +18,7 @@ // drivers — descubrimiento de hardware y E/S de disco asincrona por // interrupcion: el bus PCI y el virtio-blk (Fases 6.1, 6.2). // almacen — el grafo de objetos direccionado por contenido (Fase 6.1c). +// manifiesto — el Manifiesto de Genesis: que apps nacen del grafo (Fase 7). // memory — el heap dinamico del kernel (`#[global_allocator]`). // async_system — el reactor cooperativo: ejecutor, tareas, wakers, teclado // y el reloj que marca el compas de los fotogramas (Fase 5). @@ -50,6 +51,7 @@ mod drivers; mod gdt; mod grafico; mod interrupts; +mod manifiesto; mod memory; mod pic; mod sync; diff --git a/renaser/kernel/src/manifiesto.rs b/renaser/kernel/src/manifiesto.rs new file mode 100644 index 0000000..d3b7467 --- /dev/null +++ b/renaser/kernel/src/manifiesto.rs @@ -0,0 +1,124 @@ +// ============================================================================= +// renaser :: kernel/src/manifiesto.rs — Fase 7 :: el Manifiesto de Génesis +// ----------------------------------------------------------------------------- +// Hasta la Fase 6, el userspace venia EMPOTRADO en el binario del kernel: +// `include_bytes!` de cada `.wasm` y regiones escritas a mano. La Fase 7 lo +// destierra: las aplicaciones pasan a ser OBJETOS DEL GRAFO, y lo que arranca +// —con que cuota, en que region— lo dicta este Manifiesto de Genesis, que +// tambien habita el grafo. El superbloque guarda su hash en un ancla propia. +// +// El kernel, al despertar: lee el ancla del superbloque, recupera el objeto +// del manifiesto, lo deserializa, y por cada `EntradaApp` recupera el objeto +// de bytecode —verificado por su hash— y lo inyecta en `wasmi`. +// +// ESTADO: andamiaje de la Fase 7a. Los tipos y la (de)serializacion estan +// completos; `cargar` y `sembrar_genesis` son esbozos — se implementan al +// abordar la 7a, cuando el superbloque gane su campo `manifiesto`. Ver +// `FASE7.md` para el plan completo. +// ============================================================================= + +// Fase 7a en construccion: el modulo aun no se cablea a `kernel_main`. El +// `allow` cae en cuanto `cargar`/`sembrar_genesis` tengan llamador real. +#![allow(dead_code)] + +use alloc::string::String; +use alloc::vec::Vec; + +use serde::{Deserialize, Serialize}; + +use crate::almacen::Hash; +use crate::grafico::RegionPantalla; + +/// Version del formato del manifiesto serializado. Independiente de la +/// version del superbloque (`almacen::VERSION`): el manifiesto es un objeto +/// del grafo, no una estructura de disco. +pub const VERSION_MANIFIESTO: u32 = 1; + +/// El Manifiesto de Genesis: la lista de aplicaciones que el kernel instancia +/// al arrancar. Vive como un objeto del grafo de objetos; el superbloque +/// guarda su hash en el campo `manifiesto`. +#[derive(Serialize, Deserialize, Clone)] +pub struct Manifiesto { + /// Version del formato — debe ser [`VERSION_MANIFIESTO`]. + pub version: u32, + /// Las aplicaciones del userspace, en orden de arranque. + pub apps: Vec, +} + +/// Una entrada del manifiesto: una aplicacion del userspace y todo lo que el +/// kernel necesita para darle vida — su bytecode, su ventana, su cuota de +/// memoria y, si lo tuviera, su ultimo estado persistido. +#[derive(Serialize, Deserialize, Clone)] +pub struct EntradaApp { + /// Nombre legible — para los rotulos de la consola y la baliza. + pub nombre: String, + /// Hash del objeto del grafo que contiene el bytecode WASM de la app. + pub bytecode: Hash, + /// Sub-region del framebuffer asignada a la app. Campos de ancho fijo + /// `u32` A PROPOSITO: esto es un formato EN DISCO. `RegionPantalla` usa + /// `usize` (ancho dependiente de plataforma) y no sirve para serializar. + pub region_x: u32, + pub region_y: u32, + pub region_ancho: u32, + pub region_alto: u32, + /// Techo de memoria lineal de la app, en bytes. Sustituye a la constante + /// global `wasm::TECHO_MEMORIA` — cada app lleva su cuota. + pub techo_memoria: u32, + /// Hash del ultimo estado persistido de la app (Fase 7c). `None` hasta + /// que la app guarde estado por primera vez. + pub estado: Option, +} + +impl EntradaApp { + /// Construye la `RegionPantalla` que el kernel entiende a partir de los + /// campos de ancho fijo del manifiesto. + pub fn region(&self) -> RegionPantalla { + RegionPantalla { + x: self.region_x as usize, + y: self.region_y as usize, + ancho: self.region_ancho as usize, + alto: self.region_alto as usize, + } + } +} + +impl Manifiesto { + /// Serializa el manifiesto a su forma binaria `postcard` — la carga util + /// del objeto del grafo que lo aloja. + pub fn serializar(&self) -> Result, &'static str> { + postcard::to_allocvec(self).map_err(|_| "manifiesto :: serializacion fallida") + } + + /// Reconstruye un manifiesto desde la carga util de su objeto. Rechaza + /// un formato de version desconocida en lugar de malinterpretarlo. + pub fn deserializar(bytes: &[u8]) -> Result { + let (manifiesto, _) = postcard::take_from_bytes::(bytes) + .map_err(|_| "manifiesto :: deserializacion fallida")?; + if manifiesto.version != VERSION_MANIFIESTO { + return Err("manifiesto :: version de formato desconocida"); + } + Ok(manifiesto) + } +} + +/// Lee el manifiesto del grafo: toma su hash del ancla del superbloque, +/// recupera el objeto y lo deserializa. `Ok(None)` si el disco aun no tiene +/// manifiesto anclado — el caller debe entonces sembrar la genesis. +/// +/// ANDAMIAJE (Fase 7a-4): depende de `almacen::manifiesto()` —el nuevo ancla +/// del superbloque— todavia por implementar (tarea 7a-2). +pub fn cargar() -> Result, &'static str> { + todo!("Fase 7a-4: leer almacen::manifiesto(), recuperar el objeto y deserializar") +} + +/// Siembra el grafo en un disco sin manifiesto: graba el bytecode de las +/// aplicaciones de genesis, compone un `Manifiesto` por defecto con sus +/// regiones y cuotas, lo graba y lo ancla en el superbloque. Devuelve el +/// hash del manifiesto recien anclado. +/// +/// ANDAMIAJE (Fase 7a-3): la semilla TRANSITORIA — en la 7a el bytecode aun +/// llega vacia `include_bytes!`; la 7b mueve la siembra al constructor de +/// imagen `boot` y elimina el empotrado del kernel. +pub fn sembrar_genesis() -> Result { + todo!("Fase 7a-3: grabar los bytecodes de genesis + el manifiesto por defecto, y anclarlo") +}