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:
Executable
BIN
Binary file not shown.
@@ -68,9 +68,9 @@ const FRANJA_CONSOLA: usize = 296;
|
||||
const FRANJA_TASKBAR: usize = 40;
|
||||
|
||||
/// Anchura de cada celda de la barra de tareas, en pixeles. Dimensionada para
|
||||
/// que las ocho apps de genesis + el lanzador + el reloj caben holgados en una
|
||||
/// pantalla de 1280 px.
|
||||
const CELDA_TASKBAR_ANCHO: usize = 130;
|
||||
/// que las nueve apps de genesis (Fase 19 anexa `pregon`) + el lanzador + el
|
||||
/// reloj caben holgados en una pantalla de 1280 px.
|
||||
const CELDA_TASKBAR_ANCHO: usize = 116;
|
||||
/// Hueco entre celdas adyacentes de la barra.
|
||||
const CELDA_TASKBAR_HUECO: usize = 6;
|
||||
/// Margen izquierdo y derecho de la barra de tareas.
|
||||
|
||||
@@ -37,7 +37,10 @@ const PAQUETE_MAX: usize = 1600;
|
||||
const PROFUNDIDAD_COLA: usize = 16;
|
||||
|
||||
/// EtherType experimental (rango 0x88B5-0x88B6, reservado por IEEE para uso
|
||||
/// local). renaser lo usaria si quisiera definir su propio protocolo.
|
||||
/// local). renaser lo usaria si quisiera definir su propio protocolo. Desde
|
||||
/// la Fase 19 lo usa `pregon` desde el userspace; el kernel lo conserva como
|
||||
/// referencia para diagnosticos y futuros protocolos nativos.
|
||||
#[allow(dead_code)]
|
||||
pub const ETHER_TYPE_RENASER: u16 = 0x88B5;
|
||||
/// EtherType de ARP.
|
||||
pub const ETHER_TYPE_ARP: u16 = 0x0806;
|
||||
@@ -190,6 +193,7 @@ pub fn enviar(frame: &[u8]) -> Result<(), &'static str> {
|
||||
/// Drena los paquetes RX pendientes y aplica `callback` a cada uno. Cada
|
||||
/// bufer se recicla a la cola RX al terminar — el dispositivo tiene siempre
|
||||
/// receptores listos para la proxima IRQ.
|
||||
#[allow(dead_code)]
|
||||
pub fn drenar_rx<F: FnMut(&[u8])>(mut callback: F) {
|
||||
let Some(tarjeta) = TARJETA.get() else {
|
||||
return;
|
||||
@@ -211,6 +215,31 @@ pub fn drenar_rx<F: FnMut(&[u8])>(mut callback: F) {
|
||||
});
|
||||
}
|
||||
|
||||
/// Recibe UN paquete: copia su contenido en `buf` y devuelve los bytes
|
||||
/// copiados, o `0` si no hay paquete pendiente. La interfaz que el host
|
||||
/// expone a los apps via `sys_net_recibir` — un paquete por llamada (Fase 19).
|
||||
pub fn recibir_en(buf: &mut [u8]) -> usize {
|
||||
let Some(tarjeta) = TARJETA.get() else {
|
||||
return 0;
|
||||
};
|
||||
interrupts::without_interrupts(|| {
|
||||
let mut tarjeta = tarjeta.lock();
|
||||
if !tarjeta.0.can_recv() {
|
||||
return 0;
|
||||
}
|
||||
let rx = match tarjeta.0.receive() {
|
||||
Ok(r) => r,
|
||||
Err(_) => return 0,
|
||||
};
|
||||
let pkt = rx.packet();
|
||||
let n = pkt.len().min(buf.len());
|
||||
buf[..n].copy_from_slice(&pkt[..n]);
|
||||
let _ = tarjeta.0.recycle_rx_buffer(rx);
|
||||
PAQUETES_RX.fetch_add(1, Ordering::Relaxed);
|
||||
n
|
||||
})
|
||||
}
|
||||
|
||||
// =============================================================================
|
||||
// Composicion de un ARP request — el primer paquete que renaser saluda
|
||||
// =============================================================================
|
||||
|
||||
@@ -174,9 +174,10 @@ async fn tarea_compositor() {
|
||||
}
|
||||
}
|
||||
|
||||
/// FASE 18 — la prueba viva del enlace de red. Envia un ARP request al
|
||||
/// gateway de QEMU (10.0.2.2) y registra por COM1 cada paquete que llegue de
|
||||
/// vuelta. El primer "hola" de renaser hacia el exterior.
|
||||
/// FASE 18/19 — la primera voz del kernel hacia la red. Envia un ARP request
|
||||
/// al gateway de QEMU para anunciarse, y termina: a partir de la Fase 19, los
|
||||
/// apps drenan la cola RX por su cuenta via `sys_net_recibir`, asi que el
|
||||
/// kernel no le quita paquetes a nadie.
|
||||
async fn tarea_red(mac: drivers::red::Mac) {
|
||||
// Dejar un par de fotogramas para que la cola RX se estabilice.
|
||||
for _ in 0..10 {
|
||||
@@ -199,27 +200,7 @@ async fn tarea_red(mac: drivers::red::Mac) {
|
||||
let _ = writeln!(baliza::Serie, "red :: envio fallido :: {motivo}");
|
||||
}
|
||||
}
|
||||
// Loop perpetuo: drenar la cola RX y registrar cada paquete en COM1.
|
||||
loop {
|
||||
async_system::reloj::EsperaFrame::nueva().await;
|
||||
drivers::red::drenar_rx(|payload| {
|
||||
if payload.len() < 14 {
|
||||
return;
|
||||
}
|
||||
let etype = u16::from_be_bytes([payload[12], payload[13]]);
|
||||
let src = &payload[6..12];
|
||||
let dst = &payload[0..6];
|
||||
let _ = writeln!(
|
||||
baliza::Serie,
|
||||
"red :: RX {} bytes :: dst={:02x}:{:02x}:{:02x}:{:02x}:{:02x}:{:02x} \
|
||||
src={:02x}:{:02x}:{:02x}:{:02x}:{:02x}:{:02x} type={:#06x}",
|
||||
payload.len(),
|
||||
dst[0], dst[1], dst[2], dst[3], dst[4], dst[5],
|
||||
src[0], src[1], src[2], src[3], src[4], src[5],
|
||||
etype,
|
||||
);
|
||||
});
|
||||
}
|
||||
// La tarea termina aqui. Los apps se encargan del trafico desde ahora.
|
||||
}
|
||||
|
||||
/// FASE 6.2 — la prueba viva de la E/S asincrona. Esta tarea del reactor lee el
|
||||
|
||||
@@ -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(())
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user