feat(renaser): Fase 7b — boot siembra la imagen, muere el include_bytes!

El kernel deja de empotrar el userspace por completo. Ya no carga ni un
solo .wasm: es boot quien siembra el disco con el grafo poblado.

- kernel/almacen.rs y manifiesto.rs migran al nucleo compartido `formato`
  (tipos, postcard, BLAKE3, trazado de registros). El kernel pierde los
  include_bytes!, genesis() y sembrar_genesis().
- boot::sembrar_grafo siembra un disco virgen con el bytecode de las apps
  (deduplicado) y el Manifiesto de Genesis anclado en el superbloque.
- cargar_userspace sin rama de siembra; wasm/mod.rs sin TECHO_MEMORIA.
- alias `cargo kernel` -> --manifest-path (esquiva un ICE de cargo con
  formato compartido entre el kernel y boot via artifact-dep).

Verificado en QEMU (screendump): disco virgen -> boot siembra 5 objetos,
el kernel monta su grafo; segundo arranque -> boot respeta el disco, la
cronista persiste. formato: 5/5 pruebas.

Nota: el crate `formato` y los 3 Cargo.toml entraron antes en 43e6b32 por
un `git add -A` de un trabajo concurrente; este commit cierra el resto.

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
This commit is contained in:
sergio
2026-05-22 18:29:23 +00:00
parent 43e6b32e15
commit 7695dbf3ce
13 changed files with 415 additions and 342 deletions
+40 -89
View File
@@ -16,63 +16,27 @@
// El disco se organiza como un LOG: el sector 0 es el superbloque —el ancla
// del grafo—, y tras el se anexan los registros de objetos, uno tras otro. Un
// indice en memoria (hash -> sector) se reconstruye al arrancar recorriendo el
// log. La serializacion la hace `postcard`: binaria, compacta, determinista.
// log.
//
// El FORMATO en disco —los tipos `Objeto`/`SuperBloque`, su (de)serializacion
// `postcard`, el hash BLAKE3 y el trazado de cada registro— ya no vive aqui:
// habita la crate `formato` (Fase 7b), un nucleo `no_std` COMPARTIDO con el
// constructor de imagen `boot`. Este modulo es solo el almacen VIVO: el
// cursor, el indice y la E/S contra el disco virtio-blk.
// =============================================================================
use alloc::collections::BTreeMap;
use alloc::vec;
use alloc::vec::Vec;
use serde::{Deserialize, Serialize};
use spin::{Mutex, Once};
use crate::drivers::disco::{self, TAM_SECTOR};
use crate::drivers::disco;
/// Firma magica del superbloque — «RENASer GRaFo». Distingue un disco de
/// renaser de uno virgen o ajeno.
const MAGIA: [u8; 8] = *b"RENASGRF";
/// Version del formato en disco. Un disco con otra version se reformatea.
/// 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.
const MAX_OBJETO: usize = 1024 * 1024;
/// El identificador de un objeto: el hash BLAKE3 de su forma serializada. En un
/// almacen direccionado por contenido, la identidad ES el contenido.
pub type Hash = [u8; 32];
/// Un objeto del grafo: una carga util opaca y las aristas que lo enlazan con
/// otros objetos. Los `hijos` hacen del almacen un DAG —no un arbol, no una
/// lista—: un objeto puede ser hijo de muchos, y el direccionamiento por
/// contenido garantiza que cada contenido distinto se guarda una sola vez.
#[derive(Serialize, Deserialize, Clone)]
pub struct Objeto {
/// La carga util del objeto: bytes crudos, que el kernel no interpreta.
pub datos: Vec<u8>,
/// Los hashes de los objetos hijos: las aristas salientes del DAG.
pub hijos: Vec<Hash>,
}
/// El superbloque: el sector 0 del disco. Ancla el grafo entero — dice por
/// donde continua el log, cual es el objeto raiz y cual el manifiesto.
#[derive(Serialize, Deserialize)]
struct SuperBloque {
/// Firma magica: debe ser [`MAGIA`].
magia: [u8; 8],
/// Version del formato: debe ser [`VERSION`].
version: u32,
/// Proximo sector libre del log — donde se anexara el siguiente objeto.
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 identificador y el objeto del grafo los define `formato`; se reexportan
// para que el resto del kernel siga nombrandolos `almacen::Hash` y
// `almacen::Objeto`, sin enterarse de donde viven realmente.
pub use formato::{Hash, Objeto};
/// 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.
@@ -104,12 +68,6 @@ pub struct Resumen {
pub formateado: bool,
}
/// Numero de sectores que ocupa un registro cuyo payload mide `longitud` bytes.
/// Cada registro en disco es: `[longitud: u32 LE][payload postcard][relleno 0]`.
fn sectores_registro(longitud: usize) -> u64 {
(4 + longitud).div_ceil(TAM_SECTOR) as u64
}
/// Funda el almacen de objetos: monta el disco, lee el superbloque y, si el
/// disco ya es de renaser, reconstruye el indice recorriendo el log; si es
/// virgen o ajeno, lo formatea. Toda falla se devuelve como `Err`.
@@ -120,13 +78,13 @@ pub fn init() -> Result<Resumen, &'static str> {
}
// Leer el sector 0 e intentar interpretarlo como superbloque de renaser.
let mut sector0 = [0u8; TAM_SECTOR];
let mut sector0 = [0u8; formato::TAM_SECTOR];
disco::leer_sectores(0, &mut sector0)?;
let (cursor, raiz, manifiesto, indice, formateado) =
match postcard::take_from_bytes::<SuperBloque>(&sector0) {
match formato::SuperBloque::deserializar(&sector0) {
// Disco de renaser, con la version corriente: adoptar su grafo.
Ok((sb, _)) if sb.magia == MAGIA && sb.version == VERSION => {
Ok(sb) if sb.magia == formato::MAGIA && sb.version == formato::VERSION_SUPERBLOQUE => {
let indice = reconstruir_indice(sb.cursor)?;
(sb.cursor, sb.raiz, sb.manifiesto, indice, false)
}
@@ -166,13 +124,11 @@ fn reconstruir_indice(cursor: u64) -> Result<BTreeMap<Hash, u64>, &'static str>
let mut indice = BTreeMap::new();
let mut sector: u64 = 1;
while sector < cursor {
let payload = leer_registro(sector)?;
match payload {
match leer_registro(sector)? {
// Un payload valido: hashearlo e indexarlo.
Some(payload) => {
let n = sectores_registro(payload.len());
let hash = *blake3::hash(&payload).as_bytes();
indice.insert(hash, sector);
let n = formato::sectores_registro(payload.len());
indice.insert(formato::hash(&payload), sector);
sector += n;
}
// Cabecera a cero o longitud imposible: fin (o corrupcion) del log.
@@ -186,19 +142,18 @@ fn reconstruir_indice(cursor: u64) -> Result<BTreeMap<Hash, u64>, &'static str>
/// (sin la cabecera de longitud ni el relleno). `None` si la cabecera dice
/// longitud cero —fin del log— o una longitud imposible —corrupcion—.
fn leer_registro(sector: u64) -> Result<Option<Vec<u8>>, &'static str> {
let mut cabecera = [0u8; TAM_SECTOR];
let mut cabecera = [0u8; formato::TAM_SECTOR];
disco::leer_sectores(sector, &mut cabecera)?;
let longitud =
u32::from_le_bytes([cabecera[0], cabecera[1], cabecera[2], cabecera[3]]) as usize;
if longitud == 0 || longitud > MAX_OBJETO {
return Ok(None);
}
let n = sectores_registro(longitud) as usize;
let longitud = match formato::longitud_registro(&cabecera) {
Some(longitud) => longitud,
None => return Ok(None),
};
let n = formato::sectores_registro(longitud) as usize;
// Si el registro cabe en el sector ya leido, evitar una segunda lectura.
let payload = if n == 1 {
cabecera[4..4 + longitud].to_vec()
} else {
let mut buf = vec![0u8; n * TAM_SECTOR];
let mut buf = vec![0u8; n * formato::TAM_SECTOR];
disco::leer_sectores(sector, &mut buf)?;
buf[4..4 + longitud].to_vec()
};
@@ -207,18 +162,18 @@ fn leer_registro(sector: u64) -> Result<Option<Vec<u8>>, &'static str> {
/// Graba el superbloque —el ancla del grafo— en el sector 0.
fn persistir(almacen: &Almacen) -> Result<(), &'static str> {
let sb = SuperBloque {
magia: MAGIA,
version: VERSION,
let sb = formato::SuperBloque {
magia: formato::MAGIA,
version: formato::VERSION_SUPERBLOQUE,
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 {
let bytes = sb.serializar()?;
if bytes.len() > formato::TAM_SECTOR {
return Err("el superbloque no cabe en un sector");
}
let mut sector0 = [0u8; TAM_SECTOR];
let mut sector0 = [0u8; formato::TAM_SECTOR];
sector0[..bytes.len()].copy_from_slice(&bytes);
disco::escribir_sectores(0, &sector0)
}
@@ -228,13 +183,12 @@ fn persistir(almacen: &Almacen) -> Result<(), &'static str> {
/// se devuelve el hash que ya tenia. El grafo nunca guarda dos veces lo mismo.
pub fn almacenar(datos: Vec<u8>, hijos: Vec<Hash>) -> Result<Hash, &'static str> {
let objeto = Objeto { datos, hijos };
let bytes =
postcard::to_allocvec(&objeto).map_err(|_| "no se pudo serializar el objeto")?;
if bytes.is_empty() || bytes.len() > MAX_OBJETO {
let bytes = objeto.serializar()?;
if bytes.is_empty() || bytes.len() > formato::MAX_OBJETO {
return Err("el objeto tiene un tamaño invalido");
}
// La identidad del objeto: el hash de su forma serializada.
let hash = *blake3::hash(&bytes).as_bytes();
let hash = formato::hash(&bytes);
let mutex = ALMACEN.get().ok_or("almacen no inicializado")?;
let mut almacen = mutex.lock();
@@ -245,16 +199,14 @@ pub fn almacenar(datos: Vec<u8>, hijos: Vec<Hash>) -> Result<Hash, &'static str>
}
// Reservar los sectores del registro al final del log.
let n = sectores_registro(bytes.len());
let n = formato::sectores_registro(bytes.len());
if almacen.cursor + n > almacen.capacidad {
return Err("el grafo de objetos esta lleno");
}
let sector = almacen.cursor;
// Componer el registro: [longitud][payload][relleno a cero] y grabarlo.
let mut registro = vec![0u8; n as usize * TAM_SECTOR];
registro[0..4].copy_from_slice(&(bytes.len() as u32).to_le_bytes());
registro[4..4 + bytes.len()].copy_from_slice(&bytes);
// Componer el registro [longitud][payload][relleno] y grabarlo.
let registro = formato::componer_registro(&bytes);
disco::escribir_sectores(sector, &registro)?;
// El objeto ya esta en disco: avanzar el cursor, indexarlo y RE-anclar el
@@ -279,12 +231,10 @@ pub fn recuperar(hash: &Hash) -> Result<Option<Objeto>, &'static str> {
let payload = leer_registro(sector)?.ok_or("registro de objeto corrupto")?;
// Verificacion de integridad: el contenido leido DEBE rehashear al hash
// pedido. Si no, el disco ha mentido — y se delata.
if *blake3::hash(&payload).as_bytes() != *hash {
if formato::hash(&payload) != *hash {
return Err("el objeto no supero la verificacion de integridad");
}
let (objeto, _) = postcard::take_from_bytes::<Objeto>(&payload)
.map_err(|_| "no se pudo deserializar el objeto")?;
Ok(Some(objeto))
Ok(Some(Objeto::deserializar(&payload)?))
}
/// El hash del objeto raiz del grafo, si lo hay.
@@ -309,6 +259,7 @@ pub fn manifiesto() -> Option<Hash> {
/// Ancla un objeto como el Manifiesto de Genesis y graba el cambio en el
/// superbloque. Gemelo de [`fijar_raiz`].
#[allow(dead_code)] // Lo usara la Fase 7c (persistencia inter-sesion).
pub fn fijar_manifiesto(hash: Hash) -> Result<(), &'static str> {
let mutex = ALMACEN.get().ok_or("almacen no inicializado")?;
let mut almacen = mutex.lock();
+14 -18
View File
@@ -134,7 +134,7 @@ async fn tarea_sonda_disco() {
/// 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();
let region = manifiesto::region(entrada);
// 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.
@@ -163,24 +163,20 @@ fn reportar(linea: &str) {
}
/// 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.
/// Genesis que `boot` sembro en la imagen de disco y, 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
}
},
// Disco sin manifiesto anclado: `boot` no lo sembro. El kernel se
// levanta sin userspace —pero se levanta—; en la practica, ninguna
// imagen forjada por `boot` llega aqui sin su Manifiesto de Genesis.
Ok(None) => {
reportar("manifiesto :: el disco no trae uno -- el kernel se levanta solo");
None
}
Err(motivo) => {
reportar(&format!("manifiesto :: carga fallida -- {motivo}"));
None
@@ -333,10 +329,10 @@ fn kernel_main(boot_info: &'static mut BootInfo) -> ! {
// --- 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
// Manifiesto de Genesis que `boot` sembro en la imagen de disco 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
// discola, la glotona y la cronista) nacen del disco, no del
// binario del kernel.
//
// Las interrupciones se habilitan AHORA: el temporizador marcara el
+30 -192
View File
@@ -3,216 +3,54 @@
// -----------------------------------------------------------------------------
// 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.
// desterro: las aplicaciones son 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: Fase 7b. El kernel ya NO empotra una sola app. La siembra de la
// imagen —grabar los objetos de bytecode y el manifiesto en un disco virgen—
// la hace por completo el constructor de imagen `boot`, en el anfitrion. Este
// modulo se reduce a su esencia: LEER el manifiesto del grafo al arrancar.
//
// 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.
// Los tipos `Manifiesto` / `EntradaApp` y su (de)serializacion viven en la
// crate `formato`, el nucleo `no_std` compartido con `boot`. Aqui solo queda
// lo que es del kernel: recuperar el manifiesto del grafo y traducir las
// regiones de su formato en disco (`u32`) a la `RegionPantalla` del kernel.
// =============================================================================
use alloc::string::String;
use alloc::vec::Vec;
use serde::{Deserialize, Serialize};
use crate::almacen::Hash;
use crate::almacen;
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;
// Los tipos del manifiesto los define `formato`; se reexportan para que el
// resto del kernel los nombre `manifiesto::EntradaApp` / `manifiesto::Manifiesto`.
pub use formato::{EntradaApp, Manifiesto};
/// 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<EntradaApp>,
}
/// 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<Hash>,
}
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<Vec<u8>, &'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<Manifiesto, &'static str> {
let (manifiesto, _) = postcard::take_from_bytes::<Manifiesto>(bytes)
.map_err(|_| "manifiesto :: deserializacion fallida")?;
if manifiesto.version != VERSION_MANIFIESTO {
return Err("manifiesto :: version de formato desconocida");
}
Ok(manifiesto)
/// Traduce la sub-region de una `EntradaApp` —campos `u32` de ancho fijo, el
/// formato en disco— a la `RegionPantalla` que el kernel entiende (`usize`,
/// ancho de plataforma). El puente entre lo que el disco guarda y lo que el
/// compositor dibuja.
pub fn region(entrada: &EntradaApp) -> RegionPantalla {
RegionPantalla {
x: entrada.region_x as usize,
y: entrada.region_y as usize,
ancho: entrada.region_ancho as usize,
alto: entrada.region_alto as usize,
}
}
/// 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.
/// recupera el objeto y lo deserializa. `Ok(None)` si el disco no tiene
/// manifiesto anclado —un disco que `boot` no sembro—; el kernel se levanta
/// entonces sin userspace, pero se levanta.
pub fn cargar() -> Result<Option<Manifiesto>, &'static str> {
let hash = match crate::almacen::manifiesto() {
let hash = match 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)?
let objeto = 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 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> {
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)
}
-9
View File
@@ -32,15 +32,6 @@ const FUEL_ARRANQUE: u64 = 20_000_000;
/// milisegundos y es desalojada. Este numero ES el techo temporal del userspace.
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.
///
/// 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)]
pub enum FallaApp {