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(())
|
||||
}
|
||||
Reference in New Issue
Block a user