feat: integra renaser (kernel SASOS bare-metal) al monorepo
renaser —kernel asíncrono de espacio de direcciones único, no-POSIX, `no_std` x86_64— entra al monorepo como su PROPIO workspace de Cargo, no fusionado: usa toolchain nightly, target `x86_64-unknown-none` y `panic = "abort"`, incompatibles con los perfiles globales de brahman. - `renaser/` — copia del proyecto (sin su `.git`; el repo original conserva su historia standalone). Workspace propio con su `rust-toolchain.toml` y `.cargo/`. - `exclude = ["renaser"]` en el workspace de brahman: Cargo lo trata como ajeno. - El kernel de renaser path-depende `mirada-layout` cruzando la frontera de workspace — primer núcleo compartido. Semilla de la Fase 8 (compositor): geometría de teselado compartida, framebuffer nativo de renaser; smithay se queda en el lado Linux. Verificado: `cargo build -p boot` compila kernel + imagen UEFI con mirada-layout enlazado para bare-metal. Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
This commit is contained in:
@@ -0,0 +1,349 @@
|
||||
// =============================================================================
|
||||
// renaser :: kernel/src/wasm/env.rs — Fase 4/5/6 :: la matriz de capacidades
|
||||
// -----------------------------------------------------------------------------
|
||||
// El aislamiento de renaser no descansa en `int 0x80` ni en `sysenter`: no hay
|
||||
// vectores de syscall. Una aplicacion WASM solo puede hacer aquello para lo
|
||||
// que el kernel le haya inyectado una FUNCION DEL HOST. Esta matriz concede:
|
||||
//
|
||||
// * sys_render_frame — componer un fotograma en su region de pantalla;
|
||||
// * sys_get_scancode — consultar su canal de teclado;
|
||||
// * sys_object_put — grabar un objeto en el grafo (Fase 6.1c);
|
||||
// * sys_object_datos — leer la carga util de un objeto;
|
||||
// * sys_object_hijo — recorrer las aristas del DAG;
|
||||
// * sys_object_raiz — leer la raiz del grafo;
|
||||
// * sys_object_fijar_raiz — coronar un objeto como raiz.
|
||||
//
|
||||
// GUARDARRAIL: el kernel valida MATEMATICAMENTE todo puntero que el modulo le
|
||||
// entrega contra los limites reales de su memoria lineal. No se confia en que
|
||||
// el runtime lo haga; se verifica aqui, antes de leer o escribir un solo byte.
|
||||
//
|
||||
// DOS CLASES DE FALLO. Un puntero fuera de limites es CULPA DE LA APP: se
|
||||
// devuelve un `Error` que la ABORTA (el kernel la captura y la desaloja). Un
|
||||
// fallo del almacenamiento —disco, objeto inexistente— NO es culpa de la app:
|
||||
// se le devuelve un codigo de error negativo, y la app decide que hacer.
|
||||
// =============================================================================
|
||||
|
||||
use wasmi::{Caller, Error, Extern, Linker, Memory, StoreLimits};
|
||||
|
||||
use crate::almacen::Hash;
|
||||
use crate::async_system::teclado::CanalTeclado;
|
||||
use crate::grafico::RegionPantalla;
|
||||
|
||||
/// El estado del host adscrito al `Store` de una aplicacion: cuanto necesita
|
||||
/// una capacidad para servir a ESA app y a ninguna otra — su region de pantalla,
|
||||
/// su canal de teclado y sus cuotas de recursos. Dos apps jamas comparten nada.
|
||||
pub(crate) struct ContextoCapacidades {
|
||||
/// La sub-region de pantalla asignada a la aplicacion.
|
||||
pub(crate) region: RegionPantalla,
|
||||
/// El canal de teclado propio de la aplicacion.
|
||||
pub(crate) canal: CanalTeclado,
|
||||
/// El techo de recursos de la aplicacion — hoy, su memoria lineal maxima.
|
||||
/// `wasmi` lo consulta en cada `memory.grow` via `Store::limiter`.
|
||||
pub(crate) limites: StoreLimits,
|
||||
}
|
||||
|
||||
/// Recupera la memoria lineal exportada por el modulo. Que no la exporte es un
|
||||
/// modulo mal formado: se aborta.
|
||||
fn obtener_memoria(caller: &Caller<'_, ContextoCapacidades>) -> Result<Memory, Error> {
|
||||
caller
|
||||
.get_export("memory")
|
||||
.and_then(Extern::into_memory)
|
||||
.ok_or_else(|| Error::new("WASM :: el modulo no exporta su memoria lineal"))
|
||||
}
|
||||
|
||||
/// VALIDACION INFRANQUEABLE DE LIMITES. Comprueba que `[ptr, ptr + len)` cae
|
||||
/// entera dentro de la memoria lineal `m` y devuelve ese sub-slice. Un rango
|
||||
/// que se desborde aborta la app — el `Error` se traduce en una trampa de WASM.
|
||||
fn rango<'a>(m: &'a [u8], ptr: u32, len: usize, fallo: &'static str) -> Result<&'a [u8], Error> {
|
||||
let inicio = ptr as usize;
|
||||
match inicio.checked_add(len) {
|
||||
Some(fin) if fin <= m.len() => Ok(&m[inicio..fin]),
|
||||
_ => Err(Error::new(fallo)),
|
||||
}
|
||||
}
|
||||
|
||||
/// Lee un hash de 32 bytes de la memoria lineal, con sus limites verificados.
|
||||
fn leer_hash(m: &[u8], ptr: u32, fallo: &'static str) -> Result<Hash, Error> {
|
||||
let bytes = rango(m, ptr, 32, fallo)?;
|
||||
let mut hash = [0u8; 32];
|
||||
hash.copy_from_slice(bytes);
|
||||
Ok(hash)
|
||||
}
|
||||
|
||||
/// Inyecta en el enlazador la matriz de capacidades del modulo WASM. Todo lo
|
||||
/// que no se defina aqui le queda, al modulo, fisicamente fuera de alcance.
|
||||
///
|
||||
/// Devuelve `Err` si una capacidad no se pudo enlazar — un fallo del kernel,
|
||||
/// no de la app; aun asi se propaga como `Result` para no incendiar nada.
|
||||
pub(crate) fn enlazar_capacidades(
|
||||
enlazador: &mut Linker<ContextoCapacidades>,
|
||||
) -> Result<(), Error> {
|
||||
// --- CAPACIDAD 1 :: sys_render_frame(ptr, len) ---
|
||||
// El modulo entrega (ptr, len) hacia su PROPIA memoria lineal; el kernel
|
||||
// valida esos limites y, solo entonces, compone el fotograma DENTRO de la
|
||||
// region asignada a la app.
|
||||
enlazador.func_wrap(
|
||||
"renaser",
|
||||
"sys_render_frame",
|
||||
|caller: Caller<'_, ContextoCapacidades>, ptr: u32, len: u32| -> Result<(), Error> {
|
||||
let region = caller.data().region;
|
||||
|
||||
// El fotograma debe medir EXACTAMENTE los pixeles de la region. Un
|
||||
// tamaño distinto delata a una app que pinta fuera de su ventana:
|
||||
// se aborta antes de tocar un byte.
|
||||
let esperado = region.pixeles() * 4;
|
||||
if len as usize != esperado {
|
||||
return Err(Error::new(
|
||||
"WASM :: sys_render_frame con un fotograma ajeno a la region asignada",
|
||||
));
|
||||
}
|
||||
|
||||
let memoria = obtener_memoria(&caller)?;
|
||||
let datos: &[u8] = memoria.data(&caller);
|
||||
|
||||
// VALIDACION INFRANQUEABLE: si (ptr, len) se sale de la memoria
|
||||
// lineal del modulo, se aborta la app —no el kernel—.
|
||||
let marco = rango(
|
||||
datos,
|
||||
ptr,
|
||||
len as usize,
|
||||
"WASM :: sys_render_frame desbordo la memoria lineal del modulo",
|
||||
)?;
|
||||
|
||||
// Limites verificados: la region es segura de leer y componer.
|
||||
crate::volcar_marco_wasm(region, marco);
|
||||
Ok(())
|
||||
},
|
||||
)?;
|
||||
|
||||
// --- CAPACIDAD 2 :: sys_get_scancode() -> u32 ---
|
||||
// Expone, sin bloquear, el siguiente scancode del canal PROPIO de la app.
|
||||
enlazador.func_wrap(
|
||||
"renaser",
|
||||
"sys_get_scancode",
|
||||
|caller: Caller<'_, ContextoCapacidades>| -> u32 {
|
||||
caller.data().canal.pop().unwrap_or(0) as u32
|
||||
},
|
||||
)?;
|
||||
|
||||
// --- CAPACIDAD 3 :: sys_object_put(datos, datos_len, hijos, hijos_cnt, salida) -> i32 ---
|
||||
// Graba un objeto en el grafo. El modulo entrega, en su memoria lineal, la
|
||||
// carga util y un arreglo de `hijos_cnt` hashes de 32 bytes (las aristas).
|
||||
// El kernel escribe el hash resultante —la identidad del objeto— en
|
||||
// `salida`. Devuelve 0 si el objeto se grabo (o ya existia), -1 si el
|
||||
// almacenamiento fallo.
|
||||
enlazador.func_wrap(
|
||||
"renaser",
|
||||
"sys_object_put",
|
||||
|mut caller: Caller<'_, ContextoCapacidades>,
|
||||
datos_ptr: u32,
|
||||
datos_len: u32,
|
||||
hijos_ptr: u32,
|
||||
hijos_cnt: u32,
|
||||
salida: u32|
|
||||
-> Result<i32, Error> {
|
||||
let memoria = obtener_memoria(&caller)?;
|
||||
|
||||
// --- Leer las entradas de la memoria lineal, con limites firmes. ---
|
||||
let (datos, hijos) = {
|
||||
let m = memoria.data(&caller);
|
||||
|
||||
let datos = rango(
|
||||
m,
|
||||
datos_ptr,
|
||||
datos_len as usize,
|
||||
"WASM :: sys_object_put desbordo la memoria lineal (datos)",
|
||||
)?
|
||||
.to_vec();
|
||||
|
||||
// El arreglo de hijos: `hijos_cnt` hashes contiguos de 32 bytes.
|
||||
let bytes_hijos = (hijos_cnt as usize).checked_mul(32).ok_or_else(|| {
|
||||
Error::new("WASM :: sys_object_put con un conteo de hijos imposible")
|
||||
})?;
|
||||
let crudo = rango(
|
||||
m,
|
||||
hijos_ptr,
|
||||
bytes_hijos,
|
||||
"WASM :: sys_object_put desbordo la memoria lineal (hijos)",
|
||||
)?;
|
||||
let mut hijos: alloc::vec::Vec<Hash> =
|
||||
alloc::vec::Vec::with_capacity(hijos_cnt as usize);
|
||||
for trozo in crudo.chunks_exact(32) {
|
||||
let mut h = [0u8; 32];
|
||||
h.copy_from_slice(trozo);
|
||||
hijos.push(h);
|
||||
}
|
||||
|
||||
// Verificar que el hash de salida cabe ANTES de tocar el disco.
|
||||
rango(
|
||||
m,
|
||||
salida,
|
||||
32,
|
||||
"WASM :: sys_object_put desbordo la memoria lineal (salida)",
|
||||
)?;
|
||||
|
||||
(datos, hijos)
|
||||
};
|
||||
|
||||
// --- Grabar. Un fallo del almacen NO es culpa de la app: -1. ---
|
||||
match crate::almacen::almacenar(datos, hijos) {
|
||||
Ok(hash) => {
|
||||
let m = memoria.data_mut(&mut caller);
|
||||
m[salida as usize..salida as usize + 32].copy_from_slice(&hash);
|
||||
Ok(0)
|
||||
}
|
||||
Err(_) => Ok(-1),
|
||||
}
|
||||
},
|
||||
)?;
|
||||
|
||||
// --- CAPACIDAD 4 :: sys_object_datos(hash, salida, capacidad) -> i32 ---
|
||||
// Copia la carga util del objeto `hash` en `salida`. Devuelve el numero de
|
||||
// bytes copiados, o -1 si el objeto no existe, -2 si `capacidad` no basta,
|
||||
// -3 si el almacenamiento fallo.
|
||||
enlazador.func_wrap(
|
||||
"renaser",
|
||||
"sys_object_datos",
|
||||
|mut caller: Caller<'_, ContextoCapacidades>,
|
||||
hash_ptr: u32,
|
||||
salida: u32,
|
||||
capacidad: u32|
|
||||
-> Result<i32, Error> {
|
||||
let memoria = obtener_memoria(&caller)?;
|
||||
|
||||
let hash = {
|
||||
let m = memoria.data(&caller);
|
||||
leer_hash(
|
||||
m,
|
||||
hash_ptr,
|
||||
"WASM :: sys_object_datos desbordo la memoria lineal (hash)",
|
||||
)?
|
||||
};
|
||||
|
||||
let objeto = match crate::almacen::recuperar(&hash) {
|
||||
Ok(Some(objeto)) => objeto,
|
||||
Ok(None) => return Ok(-1),
|
||||
Err(_) => return Ok(-3),
|
||||
};
|
||||
if objeto.datos.len() > capacidad as usize {
|
||||
return Ok(-2);
|
||||
}
|
||||
|
||||
// Verificar que el destino cabe, y solo entonces copiar.
|
||||
{
|
||||
let m = memoria.data(&caller);
|
||||
rango(
|
||||
m,
|
||||
salida,
|
||||
objeto.datos.len(),
|
||||
"WASM :: sys_object_datos desbordo la memoria lineal (salida)",
|
||||
)?;
|
||||
}
|
||||
let n = objeto.datos.len();
|
||||
let m = memoria.data_mut(&mut caller);
|
||||
m[salida as usize..salida as usize + n].copy_from_slice(&objeto.datos);
|
||||
Ok(n as i32)
|
||||
},
|
||||
)?;
|
||||
|
||||
// --- CAPACIDAD 5 :: sys_object_hijo(hash, indice, salida) -> i32 ---
|
||||
// Recorre las aristas del DAG. Devuelve el NUMERO de hijos del objeto
|
||||
// `hash`; si `indice` es valido, ademas escribe el hash de ese hijo en
|
||||
// `salida`. Devuelve -1 si el objeto no existe, -3 si el almacen fallo.
|
||||
enlazador.func_wrap(
|
||||
"renaser",
|
||||
"sys_object_hijo",
|
||||
|mut caller: Caller<'_, ContextoCapacidades>,
|
||||
hash_ptr: u32,
|
||||
indice: u32,
|
||||
salida: u32|
|
||||
-> Result<i32, Error> {
|
||||
let memoria = obtener_memoria(&caller)?;
|
||||
|
||||
let hash = {
|
||||
let m = memoria.data(&caller);
|
||||
leer_hash(
|
||||
m,
|
||||
hash_ptr,
|
||||
"WASM :: sys_object_hijo desbordo la memoria lineal (hash)",
|
||||
)?
|
||||
};
|
||||
|
||||
let objeto = match crate::almacen::recuperar(&hash) {
|
||||
Ok(Some(objeto)) => objeto,
|
||||
Ok(None) => return Ok(-1),
|
||||
Err(_) => return Ok(-3),
|
||||
};
|
||||
let total = objeto.hijos.len();
|
||||
|
||||
// Si el indice apunta a un hijo real, entregar su hash.
|
||||
if let Some(hijo) = objeto.hijos.get(indice as usize) {
|
||||
{
|
||||
let m = memoria.data(&caller);
|
||||
rango(
|
||||
m,
|
||||
salida,
|
||||
32,
|
||||
"WASM :: sys_object_hijo desbordo la memoria lineal (salida)",
|
||||
)?;
|
||||
}
|
||||
let m = memoria.data_mut(&mut caller);
|
||||
m[salida as usize..salida as usize + 32].copy_from_slice(hijo);
|
||||
}
|
||||
Ok(total as i32)
|
||||
},
|
||||
)?;
|
||||
|
||||
// --- CAPACIDAD 6 :: sys_object_raiz(salida) -> i32 ---
|
||||
// Escribe en `salida` el hash de la raiz del grafo. Devuelve 1 si hay
|
||||
// raiz, 0 si el grafo aun no tiene ninguna.
|
||||
enlazador.func_wrap(
|
||||
"renaser",
|
||||
"sys_object_raiz",
|
||||
|mut caller: Caller<'_, ContextoCapacidades>, salida: u32| -> Result<i32, Error> {
|
||||
let memoria = obtener_memoria(&caller)?;
|
||||
match crate::almacen::raiz() {
|
||||
Some(hash) => {
|
||||
{
|
||||
let m = memoria.data(&caller);
|
||||
rango(
|
||||
m,
|
||||
salida,
|
||||
32,
|
||||
"WASM :: sys_object_raiz desbordo la memoria lineal (salida)",
|
||||
)?;
|
||||
}
|
||||
let m = memoria.data_mut(&mut caller);
|
||||
m[salida as usize..salida as usize + 32].copy_from_slice(&hash);
|
||||
Ok(1)
|
||||
}
|
||||
None => Ok(0),
|
||||
}
|
||||
},
|
||||
)?;
|
||||
|
||||
// --- CAPACIDAD 7 :: sys_object_fijar_raiz(hash) -> i32 ---
|
||||
// Corona el objeto `hash` como raiz del grafo. Devuelve 0 si se logro, -3
|
||||
// si el almacenamiento fallo.
|
||||
enlazador.func_wrap(
|
||||
"renaser",
|
||||
"sys_object_fijar_raiz",
|
||||
|caller: Caller<'_, ContextoCapacidades>, hash_ptr: u32| -> Result<i32, Error> {
|
||||
let memoria = obtener_memoria(&caller)?;
|
||||
let hash = {
|
||||
let m = memoria.data(&caller);
|
||||
leer_hash(
|
||||
m,
|
||||
hash_ptr,
|
||||
"WASM :: sys_object_fijar_raiz desbordo la memoria lineal (hash)",
|
||||
)?
|
||||
};
|
||||
match crate::almacen::fijar_raiz(hash) {
|
||||
Ok(()) => Ok(0),
|
||||
Err(_) => Ok(-3),
|
||||
}
|
||||
},
|
||||
)?;
|
||||
|
||||
Ok(())
|
||||
}
|
||||
@@ -0,0 +1,200 @@
|
||||
// =============================================================================
|
||||
// renaser :: kernel/src/wasm — Fase 4/5 :: el escudo de aislamiento (WASM)
|
||||
// -----------------------------------------------------------------------------
|
||||
// Aqui renaser sustituye las costosas fronteras de hardware (la MMU, los
|
||||
// anillos de la CPU) por limites MATEMATICOS sobre el bytecode. Una aplicacion
|
||||
// WebAssembly se ejecuta en su propia memoria lineal; sus unicas puertas al
|
||||
// exterior son las capacidades que el enlazador del host le concede. Lo que
|
||||
// no este importado no existe: no hay camino fisico para ejecutarlo.
|
||||
//
|
||||
// FASE 5 :: el aislamiento deja de ser solo ESPACIAL (memoria) y pasa a ser
|
||||
// tambien TEMPORAL (tiempo de CPU). Cada `tick` se ejecuta con un presupuesto
|
||||
// estricto de COMBUSTIBLE (fuel): si una app lo agota —un bucle infinito, un
|
||||
// trabajo desmedido—, el runtime lanza una trampa, el kernel recupera el mando
|
||||
// y la desaloja. Ningun modulo, por discolo que sea, secuestra el procesador.
|
||||
// =============================================================================
|
||||
|
||||
pub mod env;
|
||||
|
||||
use wasmi::{
|
||||
CompilationMode, Config, Engine, Linker, Module, Store, StoreLimitsBuilder, TrapCode, TypedFunc,
|
||||
};
|
||||
|
||||
use crate::grafico::{Color, RegionPantalla};
|
||||
use env::ContextoCapacidades;
|
||||
|
||||
/// Combustible concedido a `init`. Cubre con holgura el pintado inicial del
|
||||
/// fondo de una region a pantalla casi completa — un gasto unico, de arranque.
|
||||
const FUEL_ARRANQUE: u64 = 20_000_000;
|
||||
|
||||
/// Combustible concedido a cada `tick`. Sobra para un fotograma honesto (unos
|
||||
/// cientos de miles de operaciones); una app en bucle infinito lo agota en
|
||||
/// 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.
|
||||
const TECHO_MEMORIA: usize = 4 * 1024 * 1024;
|
||||
|
||||
/// Por que el kernel da por terminada —desaloja— una aplicacion WASM.
|
||||
#[derive(Clone, Copy)]
|
||||
pub enum FallaApp {
|
||||
/// El modulo no se pudo cargar, validar, enlazar o instanciar.
|
||||
Carga,
|
||||
/// La aplicacion agoto su combustible dentro de un `tick`: bucle infinito
|
||||
/// o trabajo desmedido. El guardarrail TEMPORAL en accion.
|
||||
SinCombustible,
|
||||
/// La aplicacion intento crecer su memoria lineal mas alla de su cuota.
|
||||
/// El guardarrail ESPACIAL en accion.
|
||||
SinMemoria,
|
||||
/// La aplicacion ejecuto una trampa: acceso fuera de limites, instruccion
|
||||
/// `unreachable`, una capacidad violada... su propio codigo la abortó.
|
||||
Trampa,
|
||||
}
|
||||
|
||||
impl FallaApp {
|
||||
/// El color de la baliza de desalojo segun la causa de la falla: amarillo
|
||||
/// palido si reviento su techo de memoria, purpura para cualquier otra.
|
||||
pub fn color_baliza(self) -> Color {
|
||||
match self {
|
||||
FallaApp::SinMemoria => Color::DESALOJO_MEMORIA,
|
||||
_ => Color::DESALOJO,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// Una aplicacion WebAssembly viva: su estado PERSISTE entre fotogramas. A
|
||||
/// diferencia de la Fase 4 —que instanciaba y cedia el control de un gesto—,
|
||||
/// aqui la instancia se conserva y el kernel la hace avanzar `tick` a `tick`.
|
||||
pub struct AplicacionWasm {
|
||||
/// El almacen: todo el estado de ESTA instancia — su memoria lineal, sus
|
||||
/// globales y el contexto de capacidades con su region de pantalla.
|
||||
almacen: Store<ContextoCapacidades>,
|
||||
/// El punto de entrada de fotograma, ya resuelto y con seguridad de tipos.
|
||||
/// `TypedFunc` es un asa autosuficiente dentro del `Store`: conservada esta,
|
||||
/// el handle de la `Instance` no aporta nada y no se retiene.
|
||||
func_tick: TypedFunc<(), ()>,
|
||||
/// La region de pantalla de la app — su ventana, y donde se tatua su baliza
|
||||
/// de desalojo si llega a fallar.
|
||||
region: RegionPantalla,
|
||||
}
|
||||
|
||||
impl AplicacionWasm {
|
||||
/// Carga, valida, instancia y arranca una aplicacion WASM aislada, ligada a
|
||||
/// una region de pantalla. Si algo falla en el camino, se devuelve la falla
|
||||
/// en lugar de incendiar el kernel.
|
||||
///
|
||||
/// 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> {
|
||||
// 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.
|
||||
let mut config = Config::default();
|
||||
config.consume_fuel(true);
|
||||
config.compilation_mode(CompilationMode::Eager);
|
||||
let motor = Engine::new(&config);
|
||||
|
||||
// 2. Validar y traducir el modulo — ya instrumentado con fuel.
|
||||
let modulo = Module::new(&motor, bytecode).map_err(|_| FallaApp::Carga)?;
|
||||
|
||||
// 3. El almacen, con el contexto de capacidades de ESTA app: su region
|
||||
// de pantalla, su canal de teclado y su techo de memoria. El canal
|
||||
// se crea ahora pero se inscribe en la difusion de la IRQ1 al final,
|
||||
// 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)
|
||||
// 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)
|
||||
.build();
|
||||
let mut almacen = Store::new(
|
||||
&motor,
|
||||
ContextoCapacidades {
|
||||
region,
|
||||
canal,
|
||||
limites,
|
||||
},
|
||||
);
|
||||
// Ligar el limitador de recursos: `wasmi` lo consultara en cada
|
||||
// `memory.grow`, tambien durante la instanciacion.
|
||||
almacen.limiter(|contexto| &mut contexto.limites);
|
||||
// Dotar de combustible ANTES de instanciar: la instanciacion no debe
|
||||
// quedarse a cero y abortar.
|
||||
almacen.set_fuel(FUEL_ARRANQUE).map_err(|_| FallaApp::Carga)?;
|
||||
|
||||
// 4. El enlazador y la matriz de capacidades (ver `env`).
|
||||
let mut enlazador: Linker<ContextoCapacidades> = Linker::new(&motor);
|
||||
env::enlazar_capacidades(&mut enlazador).map_err(|_| FallaApp::Carga)?;
|
||||
|
||||
// 5. Instanciar, resolviendo las importaciones contra las capacidades.
|
||||
let instancia = enlazador
|
||||
.instantiate_and_start(&mut almacen, &modulo)
|
||||
.map_err(|_| FallaApp::Carga)?;
|
||||
|
||||
// 6. Resolver los dos puntos del ABI de fotograma: `init` y `tick`.
|
||||
let func_init = instancia
|
||||
.get_typed_func::<(), ()>(&almacen, "init")
|
||||
.map_err(|_| FallaApp::Carga)?;
|
||||
let func_tick = instancia
|
||||
.get_typed_func::<(), ()>(&almacen, "tick")
|
||||
.map_err(|_| FallaApp::Carga)?;
|
||||
|
||||
// 7. Arranque unico: `init` prepara el estado inicial de la aplicacion.
|
||||
almacen.set_fuel(FUEL_ARRANQUE).map_err(|_| FallaApp::Carga)?;
|
||||
func_init
|
||||
.call(&mut almacen, ())
|
||||
.map_err(|_| FallaApp::Carga)?;
|
||||
|
||||
// 8. Con la app ya cargada e instanciada, inscribir su canal de teclado
|
||||
// en la difusion de la IRQ1: desde aqui recibe cada pulsacion.
|
||||
crate::async_system::teclado::registrar_canal(&almacen.data().canal);
|
||||
|
||||
Ok(AplicacionWasm {
|
||||
almacen,
|
||||
func_tick,
|
||||
region,
|
||||
})
|
||||
}
|
||||
|
||||
/// Hace avanzar la aplicacion un fotograma. Recarga su presupuesto de
|
||||
/// combustible y le cede el control con `tick`. Si la app lo agota o ejecuta
|
||||
/// una trampa, el kernel recupera el mando y la falla se devuelve para que
|
||||
/// la tarea proceda al desalojo. El kernel nunca pierde el control.
|
||||
pub fn tick(&mut self) -> Result<(), FallaApp> {
|
||||
// Recargar el deposito: cada fotograma parte con su techo intacto.
|
||||
self.almacen
|
||||
.set_fuel(FUEL_FOTOGRAMA)
|
||||
.map_err(|_| FallaApp::Trampa)?;
|
||||
|
||||
match self.func_tick.call(&mut self.almacen, ()) {
|
||||
Ok(()) => Ok(()),
|
||||
// `as_trap_code` da un codigo publico y univoco para cada causa:
|
||||
// `OutOfFuel` pliega toda variante de agotamiento de combustible;
|
||||
// `GrowthOperationLimited` es la cuota de memoria denegada.
|
||||
Err(error) => match error.as_trap_code() {
|
||||
Some(TrapCode::OutOfFuel) => Err(FallaApp::SinCombustible),
|
||||
Some(TrapCode::GrowthOperationLimited) => Err(FallaApp::SinMemoria),
|
||||
_ => Err(FallaApp::Trampa),
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
/// La region de pantalla asignada a la aplicacion.
|
||||
pub fn region(&self) -> RegionPantalla {
|
||||
self.region
|
||||
}
|
||||
}
|
||||
|
||||
/// Reconciliacion del ciclo de vida. Cuando una `AplicacionWasm` muere —porque
|
||||
/// fue desalojada y su tarea concluyo—, su canal de teclado debe darse de baja
|
||||
/// de la difusion de la IRQ1. Sin esto, el manejador de interrupciones seguiria
|
||||
/// empujando scancodes a una cola muerta: una fuga lenta pero segura.
|
||||
impl Drop for AplicacionWasm {
|
||||
fn drop(&mut self) {
|
||||
crate::async_system::teclado::cerrar_canal(&self.almacen.data().canal);
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user