⎇ 42fee6fcbc7eb98267f5abfad803018bf43224a9
renaser/kernel/src/wasm/env.rs
Ñawpa willay
Pi imata llamkarqan
ch'uya
feat(renaser): Fase 20 — Akasha Over Ether (grafo distribuido)
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}