feat(renaser): Fase 19 — voz del userspace hacia la red (pregon)

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>
This commit is contained in:
sergio
2026-05-23 04:26:22 +00:00
parent b1be94e7c4
commit 07ab095d42
13 changed files with 586 additions and 39 deletions
+87 -1
View File
@@ -15,7 +15,10 @@
// * 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_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
@@ -482,5 +485,88 @@ pub(crate) fn enlazar_capacidades(
},
)?;
// --- 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(())
}