tawasuyu

tawasuyu/brahman

Rust 582TOML 222Markdown 73JSON 198629 Sutichaykuna

⎇ 42fee6fcbc7eb98267f5abfad803018bf43224a9 renaser/kernel/src/wasm/env.rs Rust · 578 siq'ikuna · 24.0 KB Ñawpa willay Pi imata llamkarqan ch'uya

feat(renaser): Fase 20 — Akasha Over Ether (grafo distribuido) 42fee6fcb · sergio ·

1// =============================================================================
2//  renaser :: kernel/src/wasm/env.rs — Fase 4/5/6 :: la matriz de capacidades
3// -----------------------------------------------------------------------------
4//  El aislamiento de renaser no descansa en `int 0x80` ni en `sysenter`: no hay
5//  vectores de syscall. Una aplicacion WASM solo puede hacer aquello para lo
6//  que el kernel le haya inyectado una FUNCION DEL HOST. Esta matriz concede:
7//
8//    * sys_render_frame      — componer un fotograma en su region de pantalla;
9//    * sys_get_scancode      — consultar su canal de teclado;
10//    * sys_object_put        — grabar un objeto en el grafo (Fase 6.1c);
11//    * sys_object_datos      — leer la carga util de un objeto;
12//    * sys_object_hijo       — recorrer las aristas del DAG;
13//    * sys_object_raiz       — leer la raiz del grafo;
14//    * sys_object_fijar_raiz — coronar un objeto como raiz;
15//    * sys_estado_cargar     — leer el estado persistido de la app (Fase 7c);
16//    * sys_estado_guardar    — anclar el estado persistido de la app (Fase 7c);
17//    * sys_tiempo_mono       — leer el reloj monotono del sistema (Fase 11);
18//    * sys_tono              — hacer sonar la bocina del PC (Fase 12);
19//    * sys_net_mac           — leer la MAC de la tarjeta de red (Fase 19);
20//    * sys_net_enviar        — enviar un frame Ethernet crudo (Fase 19);
21//    * sys_net_recibir       — leer el siguiente frame recibido (Fase 19;
22//                              desde la Fase 20, los frames Akasha se filtran
23//                              en el kernel y no llegan al userspace).
24//
25//  GUARDARRAIL: el kernel valida MATEMATICAMENTE todo puntero que el modulo le
26//  entrega contra los limites reales de su memoria lineal. No se confia en que
27//  el runtime lo haga; se verifica aqui, antes de leer o escribir un solo byte.
28//
29//  DOS CLASES DE FALLO. Un puntero fuera de limites es CULPA DE LA APP: se
30//  devuelve un `Error` que la ABORTA (el kernel la captura y la desaloja). Un
31//  fallo del almacenamiento —disco, objeto inexistente— NO es culpa de la app:
32//  se le devuelve un codigo de error negativo, y la app decide que hacer.
33// =============================================================================
34
35use wasmi::{Caller, Error, Extern, Linker, Memory, StoreLimits};
36
37use crate::almacen::Hash;
38use crate::async_system::teclado::CanalTeclado;
39
40/// El estado del host adscrito al `Store` de una aplicacion: cuanto necesita
41/// una capacidad para servir a ESA app y a ninguna otra — su region de pantalla,
42/// su canal de teclado y sus cuotas de recursos. Dos apps jamas comparten nada.
43pub(crate) struct ContextoCapacidades {
44    /// El tamaño natural del lienzo de la app, en pixeles. El fotograma que
45    /// entrega `sys_render_frame` mide exactamente `natural_ancho × natural_alto`;
46    /// el compositor lo cachea y lo compone, sin deformarlo, en el marco que el
47    /// teselado le asigno.
48    pub(crate) natural_ancho: usize,
49    pub(crate) natural_alto: usize,
50    /// El canal de teclado propio de la aplicacion.
51    pub(crate) canal: CanalTeclado,
52    /// El techo de recursos de la aplicacion — hoy, su memoria lineal maxima.
53    /// `wasmi` lo consulta en cada `memory.grow` via `Store::limiter`.
54    pub(crate) limites: StoreLimits,
55    /// El indice de esta app — su identidad. La usan las capacidades de estado
56    /// (Fase 7c) para hallar su `EntradaApp` del manifiesto, y el compositor
57    /// (Fase 8) para hallar su ventana en el escritorio: jamas la de otra.
58    pub(crate) indice_app: usize,
59}
60
61/// Recupera la memoria lineal exportada por el modulo. Que no la exporte es un
62/// modulo mal formado: se aborta.
63fn obtener_memoria(caller: &Caller<'_, ContextoCapacidades>) -> Result<Memory, Error> {
64    caller
65        .get_export("memory")
66        .and_then(Extern::into_memory)
67        .ok_or_else(|| Error::new("WASM :: el modulo no exporta su memoria lineal"))
68}
69
70/// VALIDACION INFRANQUEABLE DE LIMITES. Comprueba que `[ptr, ptr + len)` cae
71/// entera dentro de la memoria lineal `m` y devuelve ese sub-slice. Un rango
72/// que se desborde aborta la app — el `Error` se traduce en una trampa de WASM.
73fn rango<'a>(m: &'a [u8], ptr: u32, len: usize, fallo: &'static str) -> Result<&'a [u8], Error> {
74    let inicio = ptr as usize;
75    match inicio.checked_add(len) {
76        Some(fin) if fin <= m.len() => Ok(&m[inicio..fin]),
77        _ => Err(Error::new(fallo)),
78    }
79}
80
81/// Lee un hash de 32 bytes de la memoria lineal, con sus limites verificados.
82fn leer_hash(m: &[u8], ptr: u32, fallo: &'static str) -> Result<Hash, Error> {
83    let bytes = rango(m, ptr, 32, fallo)?;
84    let mut hash = [0u8; 32];
85    hash.copy_from_slice(bytes);
86    Ok(hash)
87}
88
89/// Inyecta en el enlazador la matriz de capacidades del modulo WASM. Todo lo
90/// que no se defina aqui le queda, al modulo, fisicamente fuera de alcance.
91///
92/// Devuelve `Err` si una capacidad no se pudo enlazar — un fallo del kernel,
93/// no de la app; aun asi se propaga como `Result` para no incendiar nada.
94pub(crate) fn enlazar_capacidades(
95    enlazador: &mut Linker<ContextoCapacidades>,
96) -> Result<(), Error> {
97    // --- CAPACIDAD 1 :: sys_render_frame(ptr, len) ---
98    // El modulo entrega (ptr, len) hacia su PROPIA memoria lineal; el kernel
99    // valida esos limites y, solo entonces, compone el fotograma DENTRO de la
100    // region asignada a la app.
101    enlazador.func_wrap(
102        "renaser",
103        "sys_render_frame",
104        |caller: Caller<'_, ContextoCapacidades>, ptr: u32, len: u32| -> Result<(), Error> {
105            let indice = caller.data().indice_app;
106            let nat_ancho = caller.data().natural_ancho;
107            let nat_alto = caller.data().natural_alto;
108
109            // El fotograma debe medir EXACTAMENTE el lienzo natural de la app.
110            // Un tamaño distinto delata a una app que pinta fuera de su lienzo:
111            // se aborta antes de tocar un byte.
112            let esperado = nat_ancho * nat_alto * 4;
113            if len as usize != esperado {
114                return Err(Error::new(
115                    "WASM :: sys_render_frame con un fotograma ajeno al lienzo natural",
116                ));
117            }
118
119            let memoria = obtener_memoria(&caller)?;
120            let datos: &[u8] = memoria.data(&caller);
121
122            // VALIDACION INFRANQUEABLE: si (ptr, len) se sale de la memoria
123            // lineal del modulo, se aborta la app —no el kernel—.
124            let fotograma = rango(
125                datos,
126                ptr,
127                len as usize,
128                "WASM :: sys_render_frame desbordo la memoria lineal del modulo",
129            )?;
130
131            // Limites verificados: el compositor cachea el fotograma —para
132            // poder recomponerlo si el escritorio se re-tesela— y lo compone,
133            // centrado, en el marco que el teselado asigno a esta app.
134            crate::compositor::presentar_fotograma(indice, fotograma);
135            Ok(())
136        },
137    )?;
138
139    // --- CAPACIDAD 2 :: sys_get_scancode() -> u32 ---
140    // Expone, sin bloquear, el siguiente scancode del canal PROPIO de la app.
141    enlazador.func_wrap(
142        "renaser",
143        "sys_get_scancode",
144        |caller: Caller<'_, ContextoCapacidades>| -> u32 {
145            caller.data().canal.pop().unwrap_or(0) as u32
146        },
147    )?;
148
149    // --- CAPACIDAD 3 :: sys_object_put(datos, datos_len, hijos, hijos_cnt, salida) -> i32 ---
150    // Graba un objeto en el grafo. El modulo entrega, en su memoria lineal, la
151    // carga util y un arreglo de `hijos_cnt` hashes de 32 bytes (las aristas).
152    // El kernel escribe el hash resultante —la identidad del objeto— en
153    // `salida`. Devuelve 0 si el objeto se grabo (o ya existia), -1 si el
154    // almacenamiento fallo.
155    enlazador.func_wrap(
156        "renaser",
157        "sys_object_put",
158        |mut caller: Caller<'_, ContextoCapacidades>,
159         datos_ptr: u32,
160         datos_len: u32,
161         hijos_ptr: u32,
162         hijos_cnt: u32,
163         salida: u32|
164         -> Result<i32, Error> {
165            let memoria = obtener_memoria(&caller)?;
166
167            // --- Leer las entradas de la memoria lineal, con limites firmes. ---
168            let (datos, hijos) = {
169                let m = memoria.data(&caller);
170
171                let datos = rango(
172                    m,
173                    datos_ptr,
174                    datos_len as usize,
175                    "WASM :: sys_object_put desbordo la memoria lineal (datos)",
176                )?
177                .to_vec();
178
179                // El arreglo de hijos: `hijos_cnt` hashes contiguos de 32 bytes.
180                let bytes_hijos = (hijos_cnt as usize).checked_mul(32).ok_or_else(|| {
181                    Error::new("WASM :: sys_object_put con un conteo de hijos imposible")
182                })?;
183                let crudo = rango(
184                    m,
185                    hijos_ptr,
186                    bytes_hijos,
187                    "WASM :: sys_object_put desbordo la memoria lineal (hijos)",
188                )?;
189                let mut hijos: alloc::vec::Vec<Hash> =
190                    alloc::vec::Vec::with_capacity(hijos_cnt as usize);
191                for trozo in crudo.chunks_exact(32) {
192                    let mut h = [0u8; 32];
193                    h.copy_from_slice(trozo);
194                    hijos.push(h);
195                }
196
197                // Verificar que el hash de salida cabe ANTES de tocar el disco.
198                rango(
199                    m,
200                    salida,
201                    32,
202                    "WASM :: sys_object_put desbordo la memoria lineal (salida)",
203                )?;
204
205                (datos, hijos)
206            };
207
208            // --- Grabar. Un fallo del almacen NO es culpa de la app: -1. ---
209            match crate::almacen::almacenar(datos, hijos) {
210                Ok(hash) => {
211                    let m = memoria.data_mut(&mut caller);
212                    m[salida as usize..salida as usize + 32].copy_from_slice(&hash);
213                    Ok(0)
214                }
215                Err(_) => Ok(-1),
216            }
217        },
218    )?;
219
220    // --- CAPACIDAD 4 :: sys_object_datos(hash, salida, capacidad) -> i32 ---
221    // Copia la carga util del objeto `hash` en `salida`. Devuelve el numero de
222    // bytes copiados, o -1 si el objeto no existe, -2 si `capacidad` no basta,
223    // -3 si el almacenamiento fallo.
224    enlazador.func_wrap(
225        "renaser",
226        "sys_object_datos",
227        |mut caller: Caller<'_, ContextoCapacidades>,
228         hash_ptr: u32,
229         salida: u32,
230         capacidad: u32|
231         -> Result<i32, Error> {
232            let memoria = obtener_memoria(&caller)?;
233
234            let hash = {
235                let m = memoria.data(&caller);
236                leer_hash(
237                    m,
238                    hash_ptr,
239                    "WASM :: sys_object_datos desbordo la memoria lineal (hash)",
240                )?
241            };
242
243            let objeto = match crate::almacen::recuperar(&hash) {
244                Ok(Some(objeto)) => objeto,
245                Ok(None) => return Ok(-1),
246                Err(_) => return Ok(-3),
247            };
248            if objeto.datos.len() > capacidad as usize {
249                return Ok(-2);
250            }
251
252            // Verificar que el destino cabe, y solo entonces copiar.
253            {
254                let m = memoria.data(&caller);
255                rango(
256                    m,
257                    salida,
258                    objeto.datos.len(),
259                    "WASM :: sys_object_datos desbordo la memoria lineal (salida)",
260                )?;
261            }
262            let n = objeto.datos.len();
263            let m = memoria.data_mut(&mut caller);
264            m[salida as usize..salida as usize + n].copy_from_slice(&objeto.datos);
265            Ok(n as i32)
266        },
267    )?;
268
269    // --- CAPACIDAD 5 :: sys_object_hijo(hash, indice, salida) -> i32 ---
270    // Recorre las aristas del DAG. Devuelve el NUMERO de hijos del objeto
271    // `hash`; si `indice` es valido, ademas escribe el hash de ese hijo en
272    // `salida`. Devuelve -1 si el objeto no existe, -3 si el almacen fallo.
273    enlazador.func_wrap(
274        "renaser",
275        "sys_object_hijo",
276        |mut caller: Caller<'_, ContextoCapacidades>,
277         hash_ptr: u32,
278         indice: u32,
279         salida: u32|
280         -> Result<i32, Error> {
281            let memoria = obtener_memoria(&caller)?;
282
283            let hash = {
284                let m = memoria.data(&caller);
285                leer_hash(
286                    m,
287                    hash_ptr,
288                    "WASM :: sys_object_hijo desbordo la memoria lineal (hash)",
289                )?
290            };
291
292            let objeto = match crate::almacen::recuperar(&hash) {
293                Ok(Some(objeto)) => objeto,
294                Ok(None) => return Ok(-1),
295                Err(_) => return Ok(-3),
296            };
297            let total = objeto.hijos.len();
298
299            // Si el indice apunta a un hijo real, entregar su hash.
300            if let Some(hijo) = objeto.hijos.get(indice as usize) {
301                {
302                    let m = memoria.data(&caller);
303                    rango(
304                        m,
305                        salida,
306                        32,
307                        "WASM :: sys_object_hijo desbordo la memoria lineal (salida)",
308                    )?;
309                }
310                let m = memoria.data_mut(&mut caller);
311                m[salida as usize..salida as usize + 32].copy_from_slice(hijo);
312            }
313            Ok(total as i32)
314        },
315    )?;
316
317    // --- CAPACIDAD 6 :: sys_object_raiz(salida) -> i32 ---
318    // Escribe en `salida` el hash de la raiz del grafo. Devuelve 1 si hay
319    // raiz, 0 si el grafo aun no tiene ninguna.
320    enlazador.func_wrap(
321        "renaser",
322        "sys_object_raiz",
323        |mut caller: Caller<'_, ContextoCapacidades>, salida: u32| -> Result<i32, Error> {
324            let memoria = obtener_memoria(&caller)?;
325            match crate::almacen::raiz() {
326                Some(hash) => {
327                    {
328                        let m = memoria.data(&caller);
329                        rango(
330                            m,
331                            salida,
332                            32,
333                            "WASM :: sys_object_raiz desbordo la memoria lineal (salida)",
334                        )?;
335                    }
336                    let m = memoria.data_mut(&mut caller);
337                    m[salida as usize..salida as usize + 32].copy_from_slice(&hash);
338                    Ok(1)
339                }
340                None => Ok(0),
341            }
342        },
343    )?;
344
345    // --- CAPACIDAD 7 :: sys_object_fijar_raiz(hash) -> i32 ---
346    // Corona el objeto `hash` como raiz del grafo. Devuelve 0 si se logro, -3
347    // si el almacenamiento fallo.
348    enlazador.func_wrap(
349        "renaser",
350        "sys_object_fijar_raiz",
351        |caller: Caller<'_, ContextoCapacidades>, hash_ptr: u32| -> Result<i32, Error> {
352            let memoria = obtener_memoria(&caller)?;
353            let hash = {
354                let m = memoria.data(&caller);
355                leer_hash(
356                    m,
357                    hash_ptr,
358                    "WASM :: sys_object_fijar_raiz desbordo la memoria lineal (hash)",
359                )?
360            };
361            match crate::almacen::fijar_raiz(hash) {
362                Ok(()) => Ok(0),
363                Err(_) => Ok(-3),
364            }
365        },
366    )?;
367
368    // --- CAPACIDAD 8 :: sys_estado_cargar(salida, capacidad) -> i32 ---
369    // Copia el estado persistido de ESTA app —el objeto que su `EntradaApp` del
370    // manifiesto tiene anclado— en `salida`. Devuelve el numero de bytes
371    // copiados, 0 si la app no tiene estado previo, -1 si el objeto anclado no
372    // existe, -2 si `capacidad` no basta, -3 si el almacenamiento fallo.
373    enlazador.func_wrap(
374        "renaser",
375        "sys_estado_cargar",
376        |mut caller: Caller<'_, ContextoCapacidades>,
377         salida: u32,
378         capacidad: u32|
379         -> Result<i32, Error> {
380            let indice = caller.data().indice_app;
381            // El hash del estado de esta app, segun el manifiesto vivo.
382            let hash = match crate::manifiesto::estado_de(indice) {
383                Some(hash) => hash,
384                None => return Ok(0), // Sin estado previo: nada que cargar.
385            };
386            let objeto = match crate::almacen::recuperar(&hash) {
387                Ok(Some(objeto)) => objeto,
388                Ok(None) => return Ok(-1),
389                Err(_) => return Ok(-3),
390            };
391            if objeto.datos.len() > capacidad as usize {
392                return Ok(-2);
393            }
394
395            let memoria = obtener_memoria(&caller)?;
396            // Verificar que el destino cabe, y solo entonces copiar.
397            {
398                let m = memoria.data(&caller);
399                rango(
400                    m,
401                    salida,
402                    objeto.datos.len(),
403                    "WASM :: sys_estado_cargar desbordo la memoria lineal (salida)",
404                )?;
405            }
406            let n = objeto.datos.len();
407            let m = memoria.data_mut(&mut caller);
408            m[salida as usize..salida as usize + n].copy_from_slice(&objeto.datos);
409            Ok(n as i32)
410        },
411    )?;
412
413    // --- CAPACIDAD 9 :: sys_estado_guardar(datos, datos_len) -> i32 ---
414    // Graba `datos` como el estado persistido de ESTA app: el kernel lo
415    // almacena como un objeto del grafo y ancla su hash en la `EntradaApp` de
416    // la app, re-grabando y re-anclando el manifiesto. El estado sobrevivira al
417    // reinicio. Devuelve 0 si se logro, -3 si el almacenamiento fallo.
418    enlazador.func_wrap(
419        "renaser",
420        "sys_estado_guardar",
421        |caller: Caller<'_, ContextoCapacidades>,
422         datos_ptr: u32,
423         datos_len: u32|
424         -> Result<i32, Error> {
425            let indice = caller.data().indice_app;
426            let memoria = obtener_memoria(&caller)?;
427            // Leer el estado de la memoria lineal, con limites firmes.
428            let datos = {
429                let m = memoria.data(&caller);
430                rango(
431                    m,
432                    datos_ptr,
433                    datos_len as usize,
434                    "WASM :: sys_estado_guardar desbordo la memoria lineal (datos)",
435                )?
436                .to_vec()
437            };
438            // Grabar el objeto de estado. Un fallo del almacen NO es culpa de
439            // la app: se le devuelve -3, y ella decide que hacer.
440            let hash = match crate::almacen::almacenar(datos, alloc::vec::Vec::new()) {
441                Ok(hash) => hash,
442                Err(_) => return Ok(-3),
443            };
444            // Anclarlo: muta el manifiesto vivo, lo re-graba y lo re-ancla.
445            match crate::manifiesto::fijar_estado(indice, hash) {
446                Ok(()) => Ok(0),
447                Err(_) => Ok(-3),
448            }
449        },
450    )?;
451
452    // --- CAPACIDAD 10 :: sys_tiempo_mono() -> u64 ---
453    // El reloj MONOTONO del sistema: milisegundos transcurridos desde el
454    // arranque. Le da al userspace un sentido del tiempo independiente del
455    // ritmo de los fotogramas — una app sabe CUANTO ha pasado, no solo CUANTAS
456    // veces la han llamado—. Jamas retrocede. No toca la memoria del modulo:
457    // es una lectura pura, sin puntero que validar.
458    enlazador.func_wrap(
459        "renaser",
460        "sys_tiempo_mono",
461        |_caller: Caller<'_, ContextoCapacidades>| -> u64 {
462            crate::async_system::reloj::milisegundos()
463        },
464    )?;
465
466    // --- CAPACIDAD 11 :: sys_tono(frecuencia_hz) ---
467    // Hace sonar la bocina del PC a `frecuencia_hz` (un 0 la silencia). La
468    // bocina es un recurso UNICO y global: para que dos apps no se la disputen,
469    // pertenece —como el teclado desde la Fase 8c— a la ventana ENFOCADA. Una
470    // app sin foco puede pedir un tono; sencillamente, no se oye. Y cuando el
471    // foco cambia, el compositor calla la bocina: la nueva dueña la reclamara
472    // en su proximo fotograma si quiere sonar.
473    enlazador.func_wrap(
474        "renaser",
475        "sys_tono",
476        |caller: Caller<'_, ContextoCapacidades>, frecuencia_hz: u32| {
477            // Prioridad del kernel: mientras suena una nota agendada por el
478            // sistema (acorde de bienvenida, repique al lanzar o cerrar una
479            // app, bajo de desalojo), las llamadas de los apps se ignoran. El
480            // kernel no se interrumpe a si mismo en mitad de su voz propia.
481            if crate::drivers::altavoz::kernel_sonando() {
482                return;
483            }
484            if crate::compositor::foco() == caller.data().indice_app {
485                crate::drivers::altavoz::tono(frecuencia_hz);
486            }
487        },
488    )?;
489
490    // --- CAPACIDAD 12 :: sys_net_mac(salida) -> i32 ---
491    // Copia los 6 bytes de la MAC de la tarjeta de red en `salida`. Devuelve 0
492    // si la red esta montada; -1 si no hay tarjeta o aun no se monto.
493    enlazador.func_wrap(
494        "renaser",
495        "sys_net_mac",
496        |mut caller: Caller<'_, ContextoCapacidades>, salida: u32| -> Result<i32, Error> {
497            let Some(mac) = crate::drivers::red::mac() else {
498                return Ok(-1);
499            };
500            let memoria = obtener_memoria(&caller)?;
501            {
502                let m = memoria.data(&caller);
503                rango(m, salida, 6, "WASM :: sys_net_mac desbordo la memoria lineal")?;
504            }
505            let m = memoria.data_mut(&mut caller);
506            m[salida as usize..salida as usize + 6].copy_from_slice(&mac);
507            Ok(0)
508        },
509    )?;
510
511    // --- CAPACIDAD 13 :: sys_net_enviar(ptr, len) -> i32 ---
512    // Envia un frame Ethernet crudo (cabecera + payload, sin CRC). El app
513    // construye el frame entero en su memoria lineal. Devuelve 0 si el
514    // envio se entrego al dispositivo; -1 si fallo el envio o no hay red.
515    enlazador.func_wrap(
516        "renaser",
517        "sys_net_enviar",
518        |caller: Caller<'_, ContextoCapacidades>, ptr: u32, len: u32| -> Result<i32, Error> {
519            let memoria = obtener_memoria(&caller)?;
520            let datos = memoria.data(&caller);
521            let frame = rango(
522                datos,
523                ptr,
524                len as usize,
525                "WASM :: sys_net_enviar desbordo la memoria lineal",
526            )?;
527            match crate::drivers::red::enviar(frame) {
528                Ok(()) => Ok(0),
529                Err(_) => Ok(-1),
530            }
531        },
532    )?;
533
534    // --- CAPACIDAD 14 :: sys_net_recibir(salida, capacidad) -> i32 ---
535    // Saca el siguiente frame de la cola del USUARIO y lo copia en `salida`.
536    // Desde la Fase 20, esa cola la rellena el demultiplexor del kernel
537    // (`akasha::drenar_y_demultiplexar`): los frames Akasha (`0x88B5` con
538    // payload valido) se procesan en el nucleo y NO llegan aqui; el resto
539    // del trafico —ARP, IPv4 de QEMU, futuros protocolos— si. Devuelve los
540    // bytes copiados (>0), 0 si no hay frame pendiente, o -1 si no hay red
541    // montada. La cola se vacia FIFO; si un app no llama nunca, los frames
542    // mas antiguos se descartan al desbordar (ver `akasha::COLA_USUARIO`).
543    enlazador.func_wrap(
544        "renaser",
545        "sys_net_recibir",
546        |mut caller: Caller<'_, ContextoCapacidades>,
547         salida: u32,
548         capacidad: u32|
549         -> Result<i32, Error> {
550            if crate::drivers::red::mac().is_none() {
551                return Ok(-1);
552            }
553            let memoria = obtener_memoria(&caller)?;
554            // Verificar que el destino cabe ANTES de tocar la cola.
555            {
556                let m = memoria.data(&caller);
557                rango(
558                    m,
559                    salida,
560                    capacidad as usize,
561                    "WASM :: sys_net_recibir desbordo la memoria lineal",
562                )?;
563            }
564            // Bufer kernel-side donde la cola del usuario vuelca el frame; luego
565            // se copia a la memoria del app en una sola pasada.
566            let mut buf: alloc::vec::Vec<u8> = alloc::vec![0u8; capacidad as usize];
567            let n = crate::akasha::pop_usuario(&mut buf);
568            if n == 0 {
569                return Ok(0);
570            }
571            let m = memoria.data_mut(&mut caller);
572            m[salida as usize..salida as usize + n].copy_from_slice(&buf[..n]);
573            Ok(n as i32)
574        },
575    )?;
576
577    Ok(())
578}