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