feat(renaser): Fase 7a — el userspace nace del Grafo de Objetos
El kernel deja de empotrar las apps. Las cinco aplicaciones ya no
llegan por include_bytes! en main.rs: nacen del grafo, gobernadas por
un Manifiesto de Génesis que también vive en el grafo.
- almacen: el SuperBloque gana el ancla `manifiesto: Option<Hash>`
(gemela de `raiz`, del lado del kernel) + accesores. VERSION 1→2 —
un disco v1 se reformatea.
- manifiesto.rs: implementados `cargar` (lee el manifiesto del grafo)
y `sembrar_genesis` (puebla un disco virgen con las 5 apps de
génesis). El bytecode viaja empotrado AÚN, sólo como semilla
transitoria (la Fase 7b lo mueve al constructor de imagen `boot`).
- kernel_main: `cargar_userspace` reemplaza las 5 `encender_app`
escritas a mano; `encender_app` recupera el bytecode del grafo —
`recuperar` verifica el hash, un módulo corrupto se niega y el
arranque sigue.
- wasm: el techo de memoria pasa a ser por-app (del manifiesto).
Compila limpio. Verificación en QEMU pendiente (la corre el operador):
la pantalla debe verse idéntica a la Fase 6.2 + la línea «manifiesto».
Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
diff --git a/renaser/CHANGELOG.md b/renaser/CHANGELOG.md
index 0de6bcc..076f164 100644
--- a/renaser/CHANGELOG.md
+++ b/renaser/CHANGELOG.md
@@ -480,3 +480,42 @@ con qué cuota y en qué región.
(`#![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.
+
+## Fase 7a — Apps que nacen del Grafo: el Manifiesto — 2026-05-22
+
+El kernel deja de empotrar el userspace. Las cinco aplicaciones ya no llegan
+por `include_bytes!` en `main.rs`: nacen del grafo de objetos, gobernadas por
+un Manifiesto de Génesis que también vive en el grafo.
+
+### Añadido
+- `kernel/src/manifiesto.rs` — el Manifiesto de Génesis implementado: tipos
+ `Manifiesto` / `EntradaApp`, (de)serialización `postcard`, `cargar` (lee el
+ manifiesto del grafo vía el ancla del superbloque) y `sembrar_genesis`
+ (puebla un disco virgen con las cinco apps de génesis).
+- `almacen`: el `SuperBloque` gana el ancla `manifiesto: Option<Hash>` —
+ gemela de `raiz`, pero del lado del kernel. Accesores `manifiesto()` y
+ `fijar_manifiesto()`.
+
+### Cambiado
+- `almacen::VERSION` 1 → 2: el superbloque cambia de forma. Un disco v1 se
+ reformatea al arrancar, como cualquier disco ajeno — la cuenta de la
+ cronista reinicia una vez.
+- `kernel_main`: las cinco llamadas `encender_app` escritas a mano se
+ sustituyen por `cargar_userspace`, que carga el manifiesto —o lo siembra si
+ el disco está virgen— e itera sus `EntradaApp`.
+- `encender_app` recibe una `EntradaApp` y recupera el bytecode del grafo.
+ `recuperar` ya recomputa y verifica el hash: un bytecode corrupto se niega,
+ se pinta la baliza de desalojo en su región y el arranque continúa.
+- `wasm::AplicacionWasm::cargar` recibe el techo de memoria por-app (del
+ manifiesto) en vez de la constante global `TECHO_MEMORIA`.
+
+### Notas
+- La siembra es TRANSITORIA: el bytecode aún viaja empotrado en
+ `manifiesto.rs` (`include_bytes!`) como semilla. La Fase 7b lo moverá al
+ constructor de imagen `boot` y el `include_bytes!` morirá del todo.
+
+### Pendiente de verificación
+- QEMU (la corre el operador): la pantalla debe verse idéntica a la Fase 6.2
+ —cinco apps en sus regiones—, más una línea de consola «manifiesto :: …».
+ El primer arranque reformatea el disco v1→v2 y siembra la génesis; los
+ siguientes cargan el manifiesto ya grabado en el grafo.
diff --git a/renaser/DIARIO.md b/renaser/DIARIO.md
index e183f79..4c853d8 100644
--- a/renaser/DIARIO.md
+++ b/renaser/DIARIO.md
@@ -239,6 +239,30 @@ 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.
+## Las casas dejan de venir en la maleta — el userspace nace del disco
+
+Durante seis fases, renaser cargó a sus inquilinos consigo. Las aplicaciones
+viajaban cosidas dentro del propio kernel, como muebles dentro de la maleta de
+quien se muda: prácticas de llevar, pero imposibles de cambiar sin rehacer la
+maleta entera.
+
+Hoy eso terminó. El kernel abrió el cuaderno que ayer era sólo molde —el
+Manifiesto— y escribió en él quiénes viven en la casa, en qué habitación y
+cuánto sitio se les presta. Luego dejó a los inquilinos donde siempre
+debieron estar: en el disco, en el tejido de objetos que perdura. Cuando
+renaser despierta, ya no desempaca nada; abre el cuaderno, va al disco, y va
+trayendo a cada uno a su cuarto.
+
+Si el disco está en blanco —una casa recién construida—, el kernel siembra él
+mismo la primera versión: escribe a los inquilinos y su cuaderno. Y si alguna
+vez encontrara a un inquilino corrompido, con la cara que no le corresponde,
+sencillamente no le abre la puerta: enciende su señal de alarma en esa
+habitación y sigue acomodando a los demás. La casa nunca se cae por una
+puerta que no quiso abrirse.
+
+A los ojos casi no cambió nada —las mismas cinco ventanas, encendiéndose—.
+Pero por dentro la mudanza fue total: las casas ya no vienen en la maleta.
+
---
*El diario continúa. La próxima página la escribirá la próxima jornada.*
diff --git a/renaser/kernel/src/almacen.rs b/renaser/kernel/src/almacen.rs
index 025ff00..95bd5de 100644
--- a/renaser/kernel/src/almacen.rs
+++ b/renaser/kernel/src/almacen.rs
@@ -33,7 +33,9 @@ use crate::drivers::disco::{self, TAM_SECTOR};
const MAGIA: [u8; 8] = *b"RENASGRF";
/// Version del formato en disco. Un disco con otra version se reformatea.
-const VERSION: u32 = 1;
+/// v2 (Fase 7) — el superbloque gana el ancla `manifiesto`; un disco v1 se
+/// reformatea al arrancar, como cualquier disco ajeno.
+const VERSION: u32 = 2;
/// Techo del tamaño de un objeto serializado: 1 MiB. Acota los buferes de E/S
/// y permite descartar un registro corrupto sin intentar leer un disparate.
@@ -56,7 +58,7 @@ pub struct Objeto {
}
/// El superbloque: el sector 0 del disco. Ancla el grafo entero — dice por
-/// donde continua el log y cual es el objeto raiz.
+/// donde continua el log, cual es el objeto raiz y cual el manifiesto.
#[derive(Serialize, Deserialize)]
struct SuperBloque {
/// Firma magica: debe ser [`MAGIA`].
@@ -67,15 +69,20 @@ struct SuperBloque {
cursor: u64,
/// El objeto raiz del DAG: el punto de entrada que el userspace fija y lee.
raiz: Option<Hash>,
+ /// El Manifiesto de Genesis (Fase 7): el objeto que dicta que apps nacen
+ /// del grafo al arrancar. Ancla del kernel, gemela de `raiz` (del userspace).
+ manifiesto: Option<Hash>,
}
-/// El estado vivo del almacen: el cursor del log, la raiz y el indice en
-/// memoria que traduce cada hash al sector donde habita su registro.
+/// El estado vivo del almacen: el cursor del log, la raiz, el manifiesto y el
+/// indice en memoria que traduce cada hash al sector donde habita su registro.
struct Almacen {
/// Proximo sector libre del log.
cursor: u64,
/// El objeto raiz del DAG.
raiz: Option<Hash>,
+ /// El objeto del Manifiesto de Genesis (Fase 7).
+ manifiesto: Option<Hash>,
/// Indice hash -> sector del registro. Se reconstruye al arrancar.
indice: BTreeMap<Hash, u64>,
/// Capacidad del disco, en sectores.
@@ -116,16 +123,16 @@ pub fn init() -> Result<Resumen, &'static str> {
let mut sector0 = [0u8; TAM_SECTOR];
disco::leer_sectores(0, &mut sector0)?;
- let (cursor, raiz, indice, formateado) =
+ let (cursor, raiz, manifiesto, indice, formateado) =
match postcard::take_from_bytes::<SuperBloque>(§or0) {
// Disco de renaser, con la version corriente: adoptar su grafo.
Ok((sb, _)) if sb.magia == MAGIA && sb.version == VERSION => {
let indice = reconstruir_indice(sb.cursor)?;
- (sb.cursor, sb.raiz, indice, false)
+ (sb.cursor, sb.raiz, sb.manifiesto, indice, false)
}
// Disco virgen, ajeno o de otra version: empezar de cero. El log
// arranca en el sector 1, justo despues del superbloque.
- _ => (1, None, BTreeMap::new(), true),
+ _ => (1, None, None, BTreeMap::new(), true),
};
let objetos = indice.len();
@@ -133,6 +140,7 @@ pub fn init() -> Result<Resumen, &'static str> {
let almacen = Almacen {
cursor,
raiz,
+ manifiesto,
indice,
capacidad,
};
@@ -204,6 +212,7 @@ fn persistir(almacen: &Almacen) -> Result<(), &'static str> {
version: VERSION,
cursor: almacen.cursor,
raiz: almacen.raiz,
+ manifiesto: almacen.manifiesto,
};
let bytes = postcard::to_allocvec(&sb).map_err(|_| "no se pudo serializar el superbloque")?;
if bytes.len() > TAM_SECTOR {
@@ -290,3 +299,19 @@ pub fn fijar_raiz(hash: Hash) -> Result<(), &'static str> {
almacen.raiz = Some(hash);
persistir(&almacen)
}
+
+/// El hash del objeto del Manifiesto de Genesis, si el disco tiene uno
+/// anclado. Gemelo de [`raiz`], pero del lado del kernel: lo lee la Fase 7
+/// para descubrir que apps poblar al arrancar.
+pub fn manifiesto() -> Option<Hash> {
+ ALMACEN.get().and_then(|mutex| mutex.lock().manifiesto)
+}
+
+/// Ancla un objeto como el Manifiesto de Genesis y graba el cambio en el
+/// superbloque. Gemelo de [`fijar_raiz`].
+pub fn fijar_manifiesto(hash: Hash) -> Result<(), &'static str> {
+ let mutex = ALMACEN.get().ok_or("almacen no inicializado")?;
+ let mut almacen = mutex.lock();
+ almacen.manifiesto = Some(hash);
+ persistir(&almacen)
+}
diff --git a/renaser/kernel/src/main.rs b/renaser/kernel/src/main.rs
index 2639674..04de05d 100644
--- a/renaser/kernel/src/main.rs
+++ b/renaser/kernel/src/main.rs
@@ -66,33 +66,9 @@ use async_system::executor::Executor;
use baliza::BALIZA_PANICO;
use consola::{Consola, CONSOLA};
use grafico::{
- codificar, reclamar_memoria_lienzo, Color, Lienzo, Pantalla, RegionPantalla, ALTO_MAX,
- ANCHO_MAX,
+ codificar, reclamar_memoria_lienzo, Color, Lienzo, Pantalla, ALTO_MAX, ANCHO_MAX,
};
-/// El modulo WASM del userspace, empotrado en el binario del kernel para esta
-/// fase de pruebas. Es un `.wasm` puro, compilado aparte para `wasm32`. La
-/// Fase 5 lo instancia DOS veces —el mismo bytecode, dos regiones distintas—
-/// para demostrar la multitarea cooperativa sobre el espacio unico.
-static APP_WASM: &[u8] = include_bytes!("../assets/app.wasm");
-
-/// La aplicacion DISCOLA: un modulo WASM cuyo `tick` cae en un bucle cerrado y
-/// jamas retorna. Existe para una sola cosa — demostrar que el guardarrail de
-/// combustible la fulmina sin despeinar al kernel ni a sus vecinas.
-static DISCOLA_WASM: &[u8] = include_bytes!("../assets/discola.wasm");
-
-/// La aplicacion GLOTONA: un modulo WASM que reclama memoria lineal sin freno.
-/// Demuestra el guardarrail ESPACIAL — el techo de memoria la desaloja con la
-/// baliza amarilla, gemela de la purpura del desalojo por combustible.
-static GLOTONA_WASM: &[u8] = include_bytes!("../assets/glotona.wasm");
-
-/// La aplicacion CRONISTA: la primera ciudadana del userspace que escribe en el
-/// almacenamiento PERSISTENTE. En cada arranque graba un objeto en el grafo
-/// —enlazado al del arranque anterior—, lo corona como raiz y pinta una celda
-/// por cada arranque registrado. El disco recuerda; la cuenta sobrevive a los
-/// reinicios. Demuestra las capacidades `sys_object_*` de la Fase 6.1c.
-static CRONISTA_WASM: &[u8] = include_bytes!("../assets/cronista.wasm");
-
/// Configuracion que el cargador `bootloader` aplicara antes de cedernos la CPU.
static CONFIG_ARRANQUE: BootloaderConfig = {
let mut config = BootloaderConfig::new_default();
@@ -152,16 +128,80 @@ async fn tarea_sonda_disco() {
consola.presentar();
}
-/// Da vida a una aplicacion del userspace: la carga en su region y, si lo
-/// logra, la despacha como tarea cooperativa del reactor. Una carga fallida se
-/// salda pintando su region con la baliza de desalojo — el kernel no se inmuta.
-fn encender_app(ejecutor: &mut Executor, bytecode: &'static [u8], region: RegionPantalla) {
- match wasm::AplicacionWasm::cargar(bytecode, region) {
+/// Da vida a una aplicacion del userspace a partir de su `EntradaApp` del
+/// manifiesto: recupera su bytecode del grafo, la carga en su region y la
+/// despacha como tarea cooperativa del reactor. Si el bytecode falta, esta
+/// corrupto, o la carga fracasa, se salda pintando la region de la app con
+/// la baliza de desalojo — el kernel no se inmuta y sigue con las demas.
+fn encender_app(ejecutor: &mut Executor, entrada: &manifiesto::EntradaApp) {
+ let region = entrada.region();
+ // Recuperar el bytecode del grafo. `recuperar` recomputa el hash del
+ // objeto y verifica su integridad: un bytecode corrupto se delata aqui
+ // —y la app se niega, no se instancia un modulo en el que no se confia.
+ let bytecode = match almacen::recuperar(&entrada.bytecode) {
+ Ok(Some(objeto)) => objeto.datos,
+ _ => {
+ consola::pintar_desalojo(region, Color::DESALOJO);
+ return;
+ }
+ };
+ match wasm::AplicacionWasm::cargar(&bytecode, region, entrada.techo_memoria as usize) {
Ok(app) => ejecutor.spawn(tarea_aplicacion(app)),
Err(_) => consola::pintar_desalojo(region, Color::DESALOJO),
}
}
+/// Escribe una linea en la consola global y la presenta. Atajo para los
+/// informes de arranque; no hace nada si la consola aun no existe.
+fn reportar(linea: &str) {
+ if let Some(consola) = CONSOLA.get() {
+ let mut consola = consola.lock();
+ consola.escribir(linea);
+ consola.escribir("\n");
+ consola.presentar();
+ }
+}
+
+/// FASE 7 :: puebla el userspace DESDE EL GRAFO. Carga el Manifiesto de
+/// Genesis; si el disco no tiene uno —disco virgen—, lo siembra y lo vuelve a
+/// cargar. Por cada `EntradaApp`, enciende su aplicacion. Toda falla se
+/// reporta a la consola y NO detiene el arranque: el kernel se levanta con
+/// las apps que pueda — o con ninguna, si el grafo no tiene userspace.
+fn cargar_userspace(ejecutor: &mut Executor) {
+ let manifiesto = match manifiesto::cargar() {
+ Ok(Some(m)) => Some(m),
+ // Disco sin manifiesto: sembrar la genesis y volver a cargarlo.
+ Ok(None) => match manifiesto::sembrar_genesis() {
+ Ok(_) => {
+ reportar("manifiesto :: genesis sembrada en disco virgen");
+ manifiesto::cargar().ok().flatten()
+ }
+ Err(motivo) => {
+ reportar(&format!("manifiesto :: siembra fallida -- {motivo}"));
+ None
+ }
+ },
+ Err(motivo) => {
+ reportar(&format!("manifiesto :: carga fallida -- {motivo}"));
+ None
+ }
+ };
+
+ match &manifiesto {
+ Some(m) => reportar(&format!(
+ "manifiesto :: {} apps nacidas del grafo",
+ m.apps.len(),
+ )),
+ None => reportar("manifiesto :: sin userspace -- el kernel se levanta solo"),
+ }
+
+ if let Some(m) = manifiesto {
+ for entrada in &m.apps {
+ encender_app(ejecutor, entrada);
+ }
+ }
+}
+
/// Localiza la mayor region de RAM libre que el cargador reporto — la cantera
/// de la que el DMA del disco tomara sus marcos fisicos.
fn mayor_region_usable(regiones: &MemoryRegions) -> Option<(u64, u64)> {
@@ -291,50 +331,19 @@ fn kernel_main(boot_info: &'static mut BootInfo) -> ! {
}
}
- // --- 7. FASE 5/6 :: levantar el reactor y poblar el userspace con CINCO
- // aplicaciones WASM concurrentes, cada una en su propia region:
- //
- // * App 1 — instancia de hello_wasm, a la izquierda, gobernada
- // por el teclado.
- // * App 2 — segunda instancia del MISMO bytecode, a la derecha:
- // un unico modulo en la RAM unificada, dos vidas aisladas.
- // * App discola — su `tick` es un bucle cerrado: el escudo de
- // COMBUSTIBLE la desaloja (baliza purpura) en su 1er fotograma.
- // * App glotona — reclama memoria sin freno: el escudo ESPACIAL
- // la desaloja (baliza amarilla) en su 1er fotograma.
- // * App cronista — escribe en el GRAFO DE OBJETOS persistente:
- // cada arranque deja un objeto enlazado al anterior y pinta una
- // celda por arranque. El disco recuerda entre reinicios.
+ // --- 7. FASE 7 :: levantar el reactor y poblar el userspace DESDE EL
+ // GRAFO. El kernel ya no empotra los modulos WASM: lee el
+ // Manifiesto de Genesis —si el disco esta virgen, lo siembra— e
+ // instancia cada `EntradaApp` recuperando su bytecode del grafo de
+ // objetos. Las cinco apps de genesis (dos instancias de hello, la
+ // discola, la glotona y la cronista) nacen ahora del disco, no del
+ // binario del kernel.
//
// Las interrupciones se habilitan AHORA: el temporizador marcara el
// compas de los fotogramas y la IRQ del teclado difundira cada
// scancode a los canales que las apps consultan. ---
let mut ejecutor = Executor::nuevo();
- encender_app(
- &mut ejecutor,
- APP_WASM,
- RegionPantalla { x: 100, y: 120, ancho: 480, alto: 560 },
- );
- encender_app(
- &mut ejecutor,
- APP_WASM,
- RegionPantalla { x: 700, y: 120, ancho: 480, alto: 560 },
- );
- encender_app(
- &mut ejecutor,
- DISCOLA_WASM,
- RegionPantalla { x: 60, y: 700, ancho: 360, alto: 80 },
- );
- encender_app(
- &mut ejecutor,
- GLOTONA_WASM,
- RegionPantalla { x: 460, y: 700, ancho: 360, alto: 80 },
- );
- encender_app(
- &mut ejecutor,
- CRONISTA_WASM,
- RegionPantalla { x: 860, y: 700, ancho: 360, alto: 80 },
- );
+ cargar_userspace(&mut ejecutor);
// FASE 6.2 :: una tarea mas del reactor — no una app WASM— que sondea el
// disco de forma ASINCRONA: la demostracion de que la IRQ del disco
// conduce la E/S sin detener a las aplicaciones visuales.
diff --git a/renaser/kernel/src/manifiesto.rs b/renaser/kernel/src/manifiesto.rs
index d3b7467..20c3714 100644
--- a/renaser/kernel/src/manifiesto.rs
+++ b/renaser/kernel/src/manifiesto.rs
@@ -11,16 +11,13 @@
// 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.
+// ESTADO: Fase 7a. Tipos, (de)serializacion, carga desde el grafo y siembra
+// de la genesis, implementados. La siembra es TRANSITORIA — el bytecode aun
+// viaja empotrado (`include_bytes!`, abajo); la Fase 7b lo movera al
+// constructor de imagen `boot` y el kernel dejara de empotrar una sola app.
+// 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;
@@ -104,21 +101,118 @@ impl 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<Option<Manifiesto>, &'static str> {
- todo!("Fase 7a-4: leer almacen::manifiesto(), recuperar el objeto y deserializar")
+ let hash = match crate::almacen::manifiesto() {
+ Some(hash) => hash,
+ None => return Ok(None),
+ };
+ // `recuperar` recomputa el hash del objeto y verifica su integridad: un
+ // manifiesto corrupto se delata aqui.
+ let objeto = crate::almacen::recuperar(&hash)?
+ .ok_or("manifiesto :: el objeto anclado no existe en el grafo")?;
+ let manifiesto = Manifiesto::deserializar(&objeto.datos)?;
+ Ok(Some(manifiesto))
+}
+
+// =============================================================================
+// La genesis — la semilla transitoria de la Fase 7a
+// -----------------------------------------------------------------------------
+// El bytecode de las apps de genesis viaja, POR AHORA, empotrado en el kernel.
+// Es el unico `include_bytes!` que sobrevive a la Fase 7a — y solo como
+// semilla: en un disco virgen, `sembrar_genesis` lo graba en el grafo una vez.
+// La Fase 7b lo movera al constructor de imagen `boot` y este bloque morira.
+// =============================================================================
+
+static APP_WASM: &[u8] = include_bytes!("../assets/app.wasm");
+static DISCOLA_WASM: &[u8] = include_bytes!("../assets/discola.wasm");
+static GLOTONA_WASM: &[u8] = include_bytes!("../assets/glotona.wasm");
+static CRONISTA_WASM: &[u8] = include_bytes!("../assets/cronista.wasm");
+
+/// Descriptor de una app de genesis: lo que el kernel sabe de ella ANTES de
+/// que exista en el grafo. `region` es `(x, y, ancho, alto)` en pixeles.
+struct AppGenesis {
+ nombre: &'static str,
+ bytecode: &'static [u8],
+ region: (u32, u32, u32, u32),
+ techo_memoria: u32,
+}
+
+/// El userspace de genesis: las cinco aplicaciones que pueblan un disco
+/// virgen, con las regiones de la Fase 6.2. `app.wasm` aparece dos veces
+/// —dos instancias del mismo bytecode—; el grafo, direccionado por contenido,
+/// lo guarda una sola vez.
+fn genesis() -> [AppGenesis; 5] {
+ let techo = crate::wasm::TECHO_MEMORIA as u32;
+ [
+ AppGenesis {
+ nombre: "hola-izq",
+ bytecode: APP_WASM,
+ region: (100, 120, 480, 560),
+ techo_memoria: techo,
+ },
+ AppGenesis {
+ nombre: "hola-der",
+ bytecode: APP_WASM,
+ region: (700, 120, 480, 560),
+ techo_memoria: techo,
+ },
+ AppGenesis {
+ nombre: "discola",
+ bytecode: DISCOLA_WASM,
+ region: (60, 700, 360, 80),
+ techo_memoria: techo,
+ },
+ AppGenesis {
+ nombre: "glotona",
+ bytecode: GLOTONA_WASM,
+ region: (460, 700, 360, 80),
+ techo_memoria: techo,
+ },
+ AppGenesis {
+ nombre: "cronista",
+ bytecode: CRONISTA_WASM,
+ region: (860, 700, 360, 80),
+ techo_memoria: techo,
+ },
+ ]
}
-/// 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.
+/// Siembra el grafo en un disco sin manifiesto: graba el bytecode de cada app
+/// de genesis como un objeto, compone un `Manifiesto` con sus regiones y
+/// cuotas, lo graba —con las aristas hacia los objetos de bytecode— y lo
+/// ancla en el superbloque. Devuelve el hash del manifiesto recien anclado.
pub fn sembrar_genesis() -> Result<Hash, &'static str> {
- todo!("Fase 7a-3: grabar los bytecodes de genesis + el manifiesto por defecto, y anclarlo")
+ let mut apps: Vec<EntradaApp> = Vec::new();
+ let mut hijos: Vec<Hash> = Vec::new();
+
+ for app in genesis() {
+ // Grabar el bytecode como objeto del grafo. Idempotente: dos
+ // instancias de la misma app comparten un unico objeto.
+ let bytecode = crate::almacen::almacenar(app.bytecode.to_vec(), Vec::new())?;
+ if !hijos.contains(&bytecode) {
+ hijos.push(bytecode);
+ }
+ let (x, y, ancho, alto) = app.region;
+ apps.push(EntradaApp {
+ nombre: String::from(app.nombre),
+ bytecode,
+ region_x: x,
+ region_y: y,
+ region_ancho: ancho,
+ region_alto: alto,
+ techo_memoria: app.techo_memoria,
+ estado: None,
+ });
+ }
+
+ // El objeto del manifiesto: sus `hijos` son los objetos de bytecode, de
+ // modo que el grafo lo lea como el nodo padre del userspace.
+ let manifiesto = Manifiesto {
+ version: VERSION_MANIFIESTO,
+ apps,
+ };
+ let bytes = manifiesto.serializar()?;
+ let hash = crate::almacen::almacenar(bytes, hijos)?;
+ crate::almacen::fijar_manifiesto(hash)?;
+ Ok(hash)
}
diff --git a/renaser/kernel/src/wasm/mod.rs b/renaser/kernel/src/wasm/mod.rs
index 5710331..318762a 100644
--- a/renaser/kernel/src/wasm/mod.rs
+++ b/renaser/kernel/src/wasm/mod.rs
@@ -35,7 +35,11 @@ const FUEL_FOTOGRAMA: u64 = 2_000_000;
/// Techo de memoria lineal por aplicacion: 4 MiB. Un modulo que intente crecer
/// su memoria mas alla es desalojado — el aislamiento ESPACIAL del userspace,
/// gemelo del techo TEMPORAL que impone el combustible.
-const TECHO_MEMORIA: usize = 4 * 1024 * 1024;
+///
+/// Desde la Fase 7 el techo es POR-APP: cada `EntradaApp` del manifiesto
+/// lleva el suyo. Esta constante es el valor por DEFECTO — el que usan las
+/// apps de genesis (ver `manifiesto::genesis`).
+pub(crate) const TECHO_MEMORIA: usize = 4 * 1024 * 1024;
/// Por que el kernel da por terminada —desaloja— una aplicacion WASM.
#[derive(Clone, Copy)]
@@ -88,7 +92,14 @@ impl AplicacionWasm {
/// El nuevo ABI del userspace exige dos exportaciones: `init` —invocada una
/// sola vez, aqui— y `tick` —un fotograma de trabajo, invocada despues por
/// el reactor en cada pulso del reloj.
- pub fn cargar(bytecode: &[u8], region: RegionPantalla) -> Result<AplicacionWasm, FallaApp> {
+ ///
+ /// `techo_memoria` es la cuota de memoria lineal de ESTA app, en bytes —
+ /// desde la Fase 7 la dicta su `EntradaApp` del manifiesto.
+ pub fn cargar(
+ bytecode: &[u8],
+ region: RegionPantalla,
+ techo_memoria: usize,
+ ) -> Result<AplicacionWasm, FallaApp> {
// 1. El motor, con metricas de combustible y compilacion ANTICIPADA: la
// traduccion del modulo ocurre ahora, de modo que el `fuel` mida
// despues solo EJECUCION, jamas compilacion diferida.
@@ -106,7 +117,7 @@ impl AplicacionWasm {
// ya con la app cargada: una carga fallida no deja canales huerfanos.
let canal = crate::async_system::teclado::crear_canal();
let limites = StoreLimitsBuilder::new()
- .memory_size(TECHO_MEMORIA)
+ .memory_size(techo_memoria)
// Una expansion denegada se convierte en TRAMPA, no en un -1 que la
// app pudiera ignorar: asi el kernel la captura y la desaloja.
.trap_on_grow_failure(true)