⎇ 43e6b32e15dea99bd57a9a11fdafa7bd9a2a236f
renaser/kernel/src/manifiesto.rs
Ñawpa willay
Pi imata llamkarqan
ch'uya
feat(renaser): Fase 7a — el userspace nace del Grafo de Objetos
1// =============================================================================
2// renaser :: kernel/src/manifiesto.rs — Fase 7 :: el Manifiesto de Génesis
3// -----------------------------------------------------------------------------
4// Hasta la Fase 6, el userspace venia EMPOTRADO en el binario del kernel:
5// `include_bytes!` de cada `.wasm` y regiones escritas a mano. La Fase 7 lo
6// destierra: las aplicaciones pasan a ser OBJETOS DEL GRAFO, y lo que arranca
7// —con que cuota, en que region— lo dicta este Manifiesto de Genesis, que
8// tambien habita el grafo. El superbloque guarda su hash en un ancla propia.
9//
10// El kernel, al despertar: lee el ancla del superbloque, recupera el objeto
11// del manifiesto, lo deserializa, y por cada `EntradaApp` recupera el objeto
12// de bytecode —verificado por su hash— y lo inyecta en `wasmi`.
13//
14// ESTADO: Fase 7a. Tipos, (de)serializacion, carga desde el grafo y siembra
15// de la genesis, implementados. La siembra es TRANSITORIA — el bytecode aun
16// viaja empotrado (`include_bytes!`, abajo); la Fase 7b lo movera al
17// constructor de imagen `boot` y el kernel dejara de empotrar una sola app.
18// Ver `FASE7.md` para el plan completo.
19// =============================================================================
20
21use alloc::string::String;
22use alloc::vec::Vec;
23
24use serde::{Deserialize, Serialize};
25
26use crate::almacen::Hash;
27use crate::grafico::RegionPantalla;
28
29/// Version del formato del manifiesto serializado. Independiente de la
30/// version del superbloque (`almacen::VERSION`): el manifiesto es un objeto
31/// del grafo, no una estructura de disco.
32pub const VERSION_MANIFIESTO: u32 = 1;
33
34/// El Manifiesto de Genesis: la lista de aplicaciones que el kernel instancia
35/// al arrancar. Vive como un objeto del grafo de objetos; el superbloque
36/// guarda su hash en el campo `manifiesto`.
37#[derive(Serialize, Deserialize, Clone)]
38pub struct Manifiesto {
39 /// Version del formato — debe ser [`VERSION_MANIFIESTO`].
40 pub version: u32,
41 /// Las aplicaciones del userspace, en orden de arranque.
42 pub apps: Vec<EntradaApp>,
43}
44
45/// Una entrada del manifiesto: una aplicacion del userspace y todo lo que el
46/// kernel necesita para darle vida — su bytecode, su ventana, su cuota de
47/// memoria y, si lo tuviera, su ultimo estado persistido.
48#[derive(Serialize, Deserialize, Clone)]
49pub struct EntradaApp {
50 /// Nombre legible — para los rotulos de la consola y la baliza.
51 pub nombre: String,
52 /// Hash del objeto del grafo que contiene el bytecode WASM de la app.
53 pub bytecode: Hash,
54 /// Sub-region del framebuffer asignada a la app. Campos de ancho fijo
55 /// `u32` A PROPOSITO: esto es un formato EN DISCO. `RegionPantalla` usa
56 /// `usize` (ancho dependiente de plataforma) y no sirve para serializar.
57 pub region_x: u32,
58 pub region_y: u32,
59 pub region_ancho: u32,
60 pub region_alto: u32,
61 /// Techo de memoria lineal de la app, en bytes. Sustituye a la constante
62 /// global `wasm::TECHO_MEMORIA` — cada app lleva su cuota.
63 pub techo_memoria: u32,
64 /// Hash del ultimo estado persistido de la app (Fase 7c). `None` hasta
65 /// que la app guarde estado por primera vez.
66 pub estado: Option<Hash>,
67}
68
69impl EntradaApp {
70 /// Construye la `RegionPantalla` que el kernel entiende a partir de los
71 /// campos de ancho fijo del manifiesto.
72 pub fn region(&self) -> RegionPantalla {
73 RegionPantalla {
74 x: self.region_x as usize,
75 y: self.region_y as usize,
76 ancho: self.region_ancho as usize,
77 alto: self.region_alto as usize,
78 }
79 }
80}
81
82impl Manifiesto {
83 /// Serializa el manifiesto a su forma binaria `postcard` — la carga util
84 /// del objeto del grafo que lo aloja.
85 pub fn serializar(&self) -> Result<Vec<u8>, &'static str> {
86 postcard::to_allocvec(self).map_err(|_| "manifiesto :: serializacion fallida")
87 }
88
89 /// Reconstruye un manifiesto desde la carga util de su objeto. Rechaza
90 /// un formato de version desconocida en lugar de malinterpretarlo.
91 pub fn deserializar(bytes: &[u8]) -> Result<Manifiesto, &'static str> {
92 let (manifiesto, _) = postcard::take_from_bytes::<Manifiesto>(bytes)
93 .map_err(|_| "manifiesto :: deserializacion fallida")?;
94 if manifiesto.version != VERSION_MANIFIESTO {
95 return Err("manifiesto :: version de formato desconocida");
96 }
97 Ok(manifiesto)
98 }
99}
100
101/// Lee el manifiesto del grafo: toma su hash del ancla del superbloque,
102/// recupera el objeto y lo deserializa. `Ok(None)` si el disco aun no tiene
103/// manifiesto anclado — el caller debe entonces sembrar la genesis.
104pub fn cargar() -> Result<Option<Manifiesto>, &'static str> {
105 let hash = match crate::almacen::manifiesto() {
106 Some(hash) => hash,
107 None => return Ok(None),
108 };
109 // `recuperar` recomputa el hash del objeto y verifica su integridad: un
110 // manifiesto corrupto se delata aqui.
111 let objeto = crate::almacen::recuperar(&hash)?
112 .ok_or("manifiesto :: el objeto anclado no existe en el grafo")?;
113 let manifiesto = Manifiesto::deserializar(&objeto.datos)?;
114 Ok(Some(manifiesto))
115}
116
117// =============================================================================
118// La genesis — la semilla transitoria de la Fase 7a
119// -----------------------------------------------------------------------------
120// El bytecode de las apps de genesis viaja, POR AHORA, empotrado en el kernel.
121// Es el unico `include_bytes!` que sobrevive a la Fase 7a — y solo como
122// semilla: en un disco virgen, `sembrar_genesis` lo graba en el grafo una vez.
123// La Fase 7b lo movera al constructor de imagen `boot` y este bloque morira.
124// =============================================================================
125
126static APP_WASM: &[u8] = include_bytes!("../assets/app.wasm");
127static DISCOLA_WASM: &[u8] = include_bytes!("../assets/discola.wasm");
128static GLOTONA_WASM: &[u8] = include_bytes!("../assets/glotona.wasm");
129static CRONISTA_WASM: &[u8] = include_bytes!("../assets/cronista.wasm");
130
131/// Descriptor de una app de genesis: lo que el kernel sabe de ella ANTES de
132/// que exista en el grafo. `region` es `(x, y, ancho, alto)` en pixeles.
133struct AppGenesis {
134 nombre: &'static str,
135 bytecode: &'static [u8],
136 region: (u32, u32, u32, u32),
137 techo_memoria: u32,
138}
139
140/// El userspace de genesis: las cinco aplicaciones que pueblan un disco
141/// virgen, con las regiones de la Fase 6.2. `app.wasm` aparece dos veces
142/// —dos instancias del mismo bytecode—; el grafo, direccionado por contenido,
143/// lo guarda una sola vez.
144fn genesis() -> [AppGenesis; 5] {
145 let techo = crate::wasm::TECHO_MEMORIA as u32;
146 [
147 AppGenesis {
148 nombre: "hola-izq",
149 bytecode: APP_WASM,
150 region: (100, 120, 480, 560),
151 techo_memoria: techo,
152 },
153 AppGenesis {
154 nombre: "hola-der",
155 bytecode: APP_WASM,
156 region: (700, 120, 480, 560),
157 techo_memoria: techo,
158 },
159 AppGenesis {
160 nombre: "discola",
161 bytecode: DISCOLA_WASM,
162 region: (60, 700, 360, 80),
163 techo_memoria: techo,
164 },
165 AppGenesis {
166 nombre: "glotona",
167 bytecode: GLOTONA_WASM,
168 region: (460, 700, 360, 80),
169 techo_memoria: techo,
170 },
171 AppGenesis {
172 nombre: "cronista",
173 bytecode: CRONISTA_WASM,
174 region: (860, 700, 360, 80),
175 techo_memoria: techo,
176 },
177 ]
178}
179
180/// Siembra el grafo en un disco sin manifiesto: graba el bytecode de cada app
181/// de genesis como un objeto, compone un `Manifiesto` con sus regiones y
182/// cuotas, lo graba —con las aristas hacia los objetos de bytecode— y lo
183/// ancla en el superbloque. Devuelve el hash del manifiesto recien anclado.
184pub fn sembrar_genesis() -> Result<Hash, &'static str> {
185 let mut apps: Vec<EntradaApp> = Vec::new();
186 let mut hijos: Vec<Hash> = Vec::new();
187
188 for app in genesis() {
189 // Grabar el bytecode como objeto del grafo. Idempotente: dos
190 // instancias de la misma app comparten un unico objeto.
191 let bytecode = crate::almacen::almacenar(app.bytecode.to_vec(), Vec::new())?;
192 if !hijos.contains(&bytecode) {
193 hijos.push(bytecode);
194 }
195 let (x, y, ancho, alto) = app.region;
196 apps.push(EntradaApp {
197 nombre: String::from(app.nombre),
198 bytecode,
199 region_x: x,
200 region_y: y,
201 region_ancho: ancho,
202 region_alto: alto,
203 techo_memoria: app.techo_memoria,
204 estado: None,
205 });
206 }
207
208 // El objeto del manifiesto: sus `hijos` son los objetos de bytecode, de
209 // modo que el grafo lo lea como el nodo padre del userspace.
210 let manifiesto = Manifiesto {
211 version: VERSION_MANIFIESTO,
212 apps,
213 };
214 let bytes = manifiesto.serializar()?;
215 let hash = crate::almacen::almacenar(bytes, hijos)?;
216 crate::almacen::fijar_manifiesto(hash)?;
217 Ok(hash)
218}