07ab095d42
Tres capacidades nuevas en wasm/env (12-14): - sys_net_mac(salida) -> i32: escribe los seis bytes del MAC del dispositivo. 0 OK, -1 si no hay red. - sys_net_enviar(ptr, len) -> i32: envia un frame Ethernet crudo. Valida rango contra la memoria lineal del modulo. - sys_net_recibir(salida, capacidad) -> i32: drena UN paquete por llamada hacia el buffer del modulo. Devuelve los bytes copiados, 0 si nada pendiente, codigos negativos diagnosticos. Añadida red::recibir_en(buf) -> usize como su contraparte del driver: gemelo cooperativo de drenar_rx que aterriza en un buffer del usuario. App nueva pregon (apps/pregon/, 4.2 KiB WASM): lienzo 480x160, tipografia 8x8 (font8x8) escalada x2. Al init pide su MAC y anuncia su presencia con un broadcast Ethernet — destino FF:FF:FF:FF:FF:FF, EtherType experimental 0x88B5, payload ASCII 'renaser :: hola desde mi red'. En cada tick drena un paquete con sys_net_recibir y muestra el titulo, el MAC propio, las cuentas TX/RX, y los datos del ultimo frame entrante. GENESIS 8 -> 9 apps (pregon en posicion 2 detras de bitacora); CELDA_TASKBAR_ANCHO 130 -> 116 px para que las nueve pestañas + lanzador + reloj caben holgadas en 1280 px. tarea_red del kernel ya no drena RX (la cola pertenece al userspace), conserva solo el envio del ARP de prueba al arrancar. Verificada en QEMU con -object filter-dump. El pcap captura tres frames en orden: (1) broadcast 88B5 de pregon con su payload, (2) ARP request del kernel, (3) ARP reply del gateway 52:55:0a:00:02:02. La consola anuncia 'manifiesto :: 9 apps nacidas del grafo'. Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
573 lines
24 KiB
Rust
573 lines
24 KiB
Rust
// =============================================================================
|
||
// 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;
|
||
// * sys_estado_cargar — leer el estado persistido de la app (Fase 7c);
|
||
// * sys_estado_guardar — anclar el estado persistido de la app (Fase 7c);
|
||
// * sys_tiempo_mono — leer el reloj monotono del sistema (Fase 11);
|
||
// * sys_tono — hacer sonar la bocina del PC (Fase 12);
|
||
// * sys_net_mac — leer la MAC de la tarjeta de red (Fase 19);
|
||
// * sys_net_enviar — enviar un frame Ethernet crudo (Fase 19);
|
||
// * sys_net_recibir — leer el siguiente frame recibido (Fase 19).
|
||
//
|
||
// 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;
|
||
|
||
/// 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 {
|
||
/// El tamaño natural del lienzo de la app, en pixeles. El fotograma que
|
||
/// entrega `sys_render_frame` mide exactamente `natural_ancho × natural_alto`;
|
||
/// el compositor lo cachea y lo compone, sin deformarlo, en el marco que el
|
||
/// teselado le asigno.
|
||
pub(crate) natural_ancho: usize,
|
||
pub(crate) natural_alto: usize,
|
||
/// 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,
|
||
/// El indice de esta app — su identidad. La usan las capacidades de estado
|
||
/// (Fase 7c) para hallar su `EntradaApp` del manifiesto, y el compositor
|
||
/// (Fase 8) para hallar su ventana en el escritorio: jamas la de otra.
|
||
pub(crate) indice_app: usize,
|
||
}
|
||
|
||
/// 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 indice = caller.data().indice_app;
|
||
let nat_ancho = caller.data().natural_ancho;
|
||
let nat_alto = caller.data().natural_alto;
|
||
|
||
// El fotograma debe medir EXACTAMENTE el lienzo natural de la app.
|
||
// Un tamaño distinto delata a una app que pinta fuera de su lienzo:
|
||
// se aborta antes de tocar un byte.
|
||
let esperado = nat_ancho * nat_alto * 4;
|
||
if len as usize != esperado {
|
||
return Err(Error::new(
|
||
"WASM :: sys_render_frame con un fotograma ajeno al lienzo natural",
|
||
));
|
||
}
|
||
|
||
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 fotograma = rango(
|
||
datos,
|
||
ptr,
|
||
len as usize,
|
||
"WASM :: sys_render_frame desbordo la memoria lineal del modulo",
|
||
)?;
|
||
|
||
// Limites verificados: el compositor cachea el fotograma —para
|
||
// poder recomponerlo si el escritorio se re-tesela— y lo compone,
|
||
// centrado, en el marco que el teselado asigno a esta app.
|
||
crate::compositor::presentar_fotograma(indice, fotograma);
|
||
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),
|
||
}
|
||
},
|
||
)?;
|
||
|
||
// --- CAPACIDAD 8 :: sys_estado_cargar(salida, capacidad) -> i32 ---
|
||
// Copia el estado persistido de ESTA app —el objeto que su `EntradaApp` del
|
||
// manifiesto tiene anclado— en `salida`. Devuelve el numero de bytes
|
||
// copiados, 0 si la app no tiene estado previo, -1 si el objeto anclado no
|
||
// existe, -2 si `capacidad` no basta, -3 si el almacenamiento fallo.
|
||
enlazador.func_wrap(
|
||
"renaser",
|
||
"sys_estado_cargar",
|
||
|mut caller: Caller<'_, ContextoCapacidades>,
|
||
salida: u32,
|
||
capacidad: u32|
|
||
-> Result<i32, Error> {
|
||
let indice = caller.data().indice_app;
|
||
// El hash del estado de esta app, segun el manifiesto vivo.
|
||
let hash = match crate::manifiesto::estado_de(indice) {
|
||
Some(hash) => hash,
|
||
None => return Ok(0), // Sin estado previo: nada que cargar.
|
||
};
|
||
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);
|
||
}
|
||
|
||
let memoria = obtener_memoria(&caller)?;
|
||
// Verificar que el destino cabe, y solo entonces copiar.
|
||
{
|
||
let m = memoria.data(&caller);
|
||
rango(
|
||
m,
|
||
salida,
|
||
objeto.datos.len(),
|
||
"WASM :: sys_estado_cargar 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 9 :: sys_estado_guardar(datos, datos_len) -> i32 ---
|
||
// Graba `datos` como el estado persistido de ESTA app: el kernel lo
|
||
// almacena como un objeto del grafo y ancla su hash en la `EntradaApp` de
|
||
// la app, re-grabando y re-anclando el manifiesto. El estado sobrevivira al
|
||
// reinicio. Devuelve 0 si se logro, -3 si el almacenamiento fallo.
|
||
enlazador.func_wrap(
|
||
"renaser",
|
||
"sys_estado_guardar",
|
||
|caller: Caller<'_, ContextoCapacidades>,
|
||
datos_ptr: u32,
|
||
datos_len: u32|
|
||
-> Result<i32, Error> {
|
||
let indice = caller.data().indice_app;
|
||
let memoria = obtener_memoria(&caller)?;
|
||
// Leer el estado de la memoria lineal, con limites firmes.
|
||
let datos = {
|
||
let m = memoria.data(&caller);
|
||
rango(
|
||
m,
|
||
datos_ptr,
|
||
datos_len as usize,
|
||
"WASM :: sys_estado_guardar desbordo la memoria lineal (datos)",
|
||
)?
|
||
.to_vec()
|
||
};
|
||
// Grabar el objeto de estado. Un fallo del almacen NO es culpa de
|
||
// la app: se le devuelve -3, y ella decide que hacer.
|
||
let hash = match crate::almacen::almacenar(datos, alloc::vec::Vec::new()) {
|
||
Ok(hash) => hash,
|
||
Err(_) => return Ok(-3),
|
||
};
|
||
// Anclarlo: muta el manifiesto vivo, lo re-graba y lo re-ancla.
|
||
match crate::manifiesto::fijar_estado(indice, hash) {
|
||
Ok(()) => Ok(0),
|
||
Err(_) => Ok(-3),
|
||
}
|
||
},
|
||
)?;
|
||
|
||
// --- CAPACIDAD 10 :: sys_tiempo_mono() -> u64 ---
|
||
// El reloj MONOTONO del sistema: milisegundos transcurridos desde el
|
||
// arranque. Le da al userspace un sentido del tiempo independiente del
|
||
// ritmo de los fotogramas — una app sabe CUANTO ha pasado, no solo CUANTAS
|
||
// veces la han llamado—. Jamas retrocede. No toca la memoria del modulo:
|
||
// es una lectura pura, sin puntero que validar.
|
||
enlazador.func_wrap(
|
||
"renaser",
|
||
"sys_tiempo_mono",
|
||
|_caller: Caller<'_, ContextoCapacidades>| -> u64 {
|
||
crate::async_system::reloj::milisegundos()
|
||
},
|
||
)?;
|
||
|
||
// --- CAPACIDAD 11 :: sys_tono(frecuencia_hz) ---
|
||
// Hace sonar la bocina del PC a `frecuencia_hz` (un 0 la silencia). La
|
||
// bocina es un recurso UNICO y global: para que dos apps no se la disputen,
|
||
// pertenece —como el teclado desde la Fase 8c— a la ventana ENFOCADA. Una
|
||
// app sin foco puede pedir un tono; sencillamente, no se oye. Y cuando el
|
||
// foco cambia, el compositor calla la bocina: la nueva dueña la reclamara
|
||
// en su proximo fotograma si quiere sonar.
|
||
enlazador.func_wrap(
|
||
"renaser",
|
||
"sys_tono",
|
||
|caller: Caller<'_, ContextoCapacidades>, frecuencia_hz: u32| {
|
||
// Prioridad del kernel: mientras suena una nota agendada por el
|
||
// sistema (acorde de bienvenida, repique al lanzar o cerrar una
|
||
// app, bajo de desalojo), las llamadas de los apps se ignoran. El
|
||
// kernel no se interrumpe a si mismo en mitad de su voz propia.
|
||
if crate::drivers::altavoz::kernel_sonando() {
|
||
return;
|
||
}
|
||
if crate::compositor::foco() == caller.data().indice_app {
|
||
crate::drivers::altavoz::tono(frecuencia_hz);
|
||
}
|
||
},
|
||
)?;
|
||
|
||
// --- CAPACIDAD 12 :: sys_net_mac(salida) -> i32 ---
|
||
// Copia los 6 bytes de la MAC de la tarjeta de red en `salida`. Devuelve 0
|
||
// si la red esta montada; -1 si no hay tarjeta o aun no se monto.
|
||
enlazador.func_wrap(
|
||
"renaser",
|
||
"sys_net_mac",
|
||
|mut caller: Caller<'_, ContextoCapacidades>, salida: u32| -> Result<i32, Error> {
|
||
let Some(mac) = crate::drivers::red::mac() else {
|
||
return Ok(-1);
|
||
};
|
||
let memoria = obtener_memoria(&caller)?;
|
||
{
|
||
let m = memoria.data(&caller);
|
||
rango(m, salida, 6, "WASM :: sys_net_mac desbordo la memoria lineal")?;
|
||
}
|
||
let m = memoria.data_mut(&mut caller);
|
||
m[salida as usize..salida as usize + 6].copy_from_slice(&mac);
|
||
Ok(0)
|
||
},
|
||
)?;
|
||
|
||
// --- CAPACIDAD 13 :: sys_net_enviar(ptr, len) -> i32 ---
|
||
// Envia un frame Ethernet crudo (cabecera + payload, sin CRC). El app
|
||
// construye el frame entero en su memoria lineal. Devuelve 0 si el
|
||
// envio se entrego al dispositivo; -1 si fallo el envio o no hay red.
|
||
enlazador.func_wrap(
|
||
"renaser",
|
||
"sys_net_enviar",
|
||
|caller: Caller<'_, ContextoCapacidades>, ptr: u32, len: u32| -> Result<i32, Error> {
|
||
let memoria = obtener_memoria(&caller)?;
|
||
let datos = memoria.data(&caller);
|
||
let frame = rango(
|
||
datos,
|
||
ptr,
|
||
len as usize,
|
||
"WASM :: sys_net_enviar desbordo la memoria lineal",
|
||
)?;
|
||
match crate::drivers::red::enviar(frame) {
|
||
Ok(()) => Ok(0),
|
||
Err(_) => Ok(-1),
|
||
}
|
||
},
|
||
)?;
|
||
|
||
// --- CAPACIDAD 14 :: sys_net_recibir(salida, capacidad) -> i32 ---
|
||
// Saca el siguiente frame de la cola RX del dispositivo y lo copia en
|
||
// `salida`. Devuelve los bytes copiados (>0), 0 si no hay frame pendiente,
|
||
// o -1 si no hay red montada. La cola RX es del dispositivo y se comparte
|
||
// entre los apps: el primero que pregunte se lleva el paquete.
|
||
enlazador.func_wrap(
|
||
"renaser",
|
||
"sys_net_recibir",
|
||
|mut caller: Caller<'_, ContextoCapacidades>,
|
||
salida: u32,
|
||
capacidad: u32|
|
||
-> Result<i32, Error> {
|
||
if crate::drivers::red::mac().is_none() {
|
||
return Ok(-1);
|
||
}
|
||
let memoria = obtener_memoria(&caller)?;
|
||
// Verificar que el destino cabe ANTES de tocar la cola.
|
||
{
|
||
let m = memoria.data(&caller);
|
||
rango(
|
||
m,
|
||
salida,
|
||
capacidad as usize,
|
||
"WASM :: sys_net_recibir desbordo la memoria lineal",
|
||
)?;
|
||
}
|
||
// Bufer kernel-side donde el driver vuelca el frame; luego se copia
|
||
// a la memoria del app en una sola pasada.
|
||
let mut buf: alloc::vec::Vec<u8> = alloc::vec![0u8; capacidad as usize];
|
||
let n = crate::drivers::red::recibir_en(&mut buf);
|
||
if n == 0 {
|
||
return Ok(0);
|
||
}
|
||
let m = memoria.data_mut(&mut caller);
|
||
m[salida as usize..salida as usize + n].copy_from_slice(&buf[..n]);
|
||
Ok(n as i32)
|
||
},
|
||
)?;
|
||
|
||
Ok(())
|
||
}
|