⎇ 07ab095d4202aa5ff30d9a109e610d517a434e74
renaser/kernel/src/drivers/red.rs
History
Who touched what
raw
feat(renaser): Fase 19 — voz del userspace hacia la red (pregon)
1// =============================================================================
2// renaser :: kernel/src/drivers/red.rs — Fase 18 :: virtio-net
3// -----------------------------------------------------------------------------
4// El kernel deja de hablar solo consigo mismo. Con el mismo patron del disco
5// —enumerar PCI, montar el transporte de virtio, ceder a `virtio-drivers` el
6// diálogo de bajo nivel— renaser abre una boca y una oreja al exterior: una
7// tarjeta de red virtio.
8//
9// En esta primera version el kernel envia un ARP request al gateway de
10// QEMU (10.0.2.2) en cuanto arranca, y registra por COM1 cada paquete que
11// recibe. No hay pila TCP/IP — solo ethernet crudo. El proximo paso natural
12// seria una capa de capacidades `sys_net_*` para que los apps tambien
13// hablen, pero esa es otra fase.
14// =============================================================================
15
16use core::sync::atomic::{AtomicU64, AtomicU8, Ordering};
17
18use spin::{Mutex, Once};
19use virtio_drivers::device::net::VirtIONet;
20use virtio_drivers::transport::pci::bus::{Command, DeviceFunction, PciRoot};
21use virtio_drivers::transport::pci::PciTransport;
22use x86_64::instructions::interrupts;
23
24use super::disco::KernelHal;
25use super::pci::CamPuertos;
26
27/// Vendor ID de VirtIO; Device IDs de un dispositivo de red (legacy + modern).
28const VENDOR_VIRTIO: u16 = 0x1AF4;
29const VIRTIO_NET_IDS: [u16; 2] = [0x1000, 0x1041];
30
31/// Tamaño maximo de paquete que reservamos por bufer (MTU 1500 + algo de holgura
32/// para cabeceras virtio y futuras VLAN).
33const PAQUETE_MAX: usize = 1600;
34
35/// Profundidad de las colas RX y TX. 16 es pequeño pero suficiente para el
36/// trafico de un demo.
37const PROFUNDIDAD_COLA: usize = 16;
38
39/// EtherType experimental (rango 0x88B5-0x88B6, reservado por IEEE para uso
40/// local). renaser lo usaria si quisiera definir su propio protocolo. Desde
41/// la Fase 19 lo usa `pregon` desde el userspace; el kernel lo conserva como
42/// referencia para diagnosticos y futuros protocolos nativos.
43#[allow(dead_code)]
44pub const ETHER_TYPE_RENASER: u16 = 0x88B5;
45/// EtherType de ARP.
46pub const ETHER_TYPE_ARP: u16 = 0x0806;
47
48/// Direccion fisica de la tarjeta de red, en seis bytes MAC.
49pub type Mac = [u8; 6];
50
51/// IP de la maquina renaser, en QEMU user-mode networking (10.0.2.0/24).
52pub const IP_RENASER: [u8; 4] = [10, 0, 2, 15];
53/// IP del gateway que QEMU expone hacia el host.
54pub const IP_GATEWAY: [u8; 4] = [10, 0, 2, 2];
55
56/// La tarjeta de red, ya montada. Envuelve a `VirtIONet` para que pueda vivir
57/// en un `static`.
58struct Tarjeta(VirtIONet<KernelHal, PciTransport, PROFUNDIDAD_COLA>);
59
60// SEGURIDAD: `Tarjeta` encierra punteros crudos a las colas virtio y al MMIO
61// del dispositivo. renaser es de un solo nucleo y todo acceso a la tarjeta se
62// serializa tras el `Mutex` global. Los accesos cooperativos se hacen con las
63// interrupciones acalladas para que la IRQ del dispositivo jamas las dispute.
64unsafe impl Send for Tarjeta {}
65
66/// La tarjeta global. Se monta una sola vez, en `montar`.
67static TARJETA: Once<Mutex<Tarjeta>> = Once::new();
68
69/// La direccion MAC que el dispositivo nos asigno, cacheada para consulta.
70static MAC: Once<Mac> = Once::new();
71
72/// La linea de IRQ asignada al dispositivo por el firmware.
73static IRQ_RED: AtomicU8 = AtomicU8::new(0);
74
75/// Cuenta de paquetes recibidos desde el arranque.
76static PAQUETES_RX: AtomicU64 = AtomicU64::new(0);
77/// Cuenta de paquetes transmitidos desde el arranque.
78static PAQUETES_TX: AtomicU64 = AtomicU64::new(0);
79
80// =============================================================================
81// Montaje
82// =============================================================================
83
84/// Enumera el bus PCI, localiza el virtio-net, monta su transporte moderno y
85/// lo deja tras el `Mutex` global. Descubre su linea de IRQ y la enruta.
86/// Devuelve la MAC que el dispositivo nos confiere. Toda falla se devuelve.
87pub fn montar() -> Result<Mac, &'static str> {
88 let mut raiz = PciRoot::new(CamPuertos);
89
90 // 1. Localizar el primer virtio-net en el bus.
91 let mut hallado: Option<DeviceFunction> = None;
92 'busqueda: for bus in 0..=255u8 {
93 for (device_function, info) in raiz.enumerate_bus(bus) {
94 if info.vendor_id == VENDOR_VIRTIO && VIRTIO_NET_IDS.contains(&info.device_id) {
95 hallado = Some(device_function);
96 break 'busqueda;
97 }
98 }
99 }
100 let device_function = hallado.ok_or("virtio-net no hallado en el bus PCI")?;
101
102 // 2. Habilitar E/S, MMIO y BUS-MASTER en la configuracion PCI.
103 raiz.set_command(
104 device_function,
105 Command::IO_SPACE | Command::MEMORY_SPACE | Command::BUS_MASTER,
106 );
107
108 // 3. Montar el transporte PCI moderno y el dispositivo de red.
109 let transporte = PciTransport::new::<KernelHal, _>(&mut raiz, device_function)
110 .map_err(|_| "no se pudo montar el transporte PCI de virtio-net")?;
111 let mut nic =
112 VirtIONet::<KernelHal, _, PROFUNDIDAD_COLA>::new(transporte, PAQUETE_MAX)
113 .map_err(|_| "no se pudo inicializar el dispositivo virtio-net")?;
114
115 let mac = nic.mac_address();
116 nic.enable_interrupts();
117
118 TARJETA.call_once(|| Mutex::new(Tarjeta(nic)));
119 MAC.call_once(|| mac);
120
121 // 4. Descubrir la linea de IRQ y enrutarla.
122 let irq = super::pci::linea_irq(device_function);
123 if (2..=15).contains(&irq) {
124 crate::interrupts::registrar_irq_red(irq);
125 crate::pic::desenmascarar(irq);
126 IRQ_RED.store(irq, Ordering::SeqCst);
127 }
128
129 Ok(mac)
130}
131
132// =============================================================================
133// IRQ
134// =============================================================================
135
136/// Punto de entrada DESDE el manejador de IRQ de la red. Acknowledge en el
137/// dispositivo —para que la linea baje— y se sale.
138pub fn atender_irq() {
139 if let Some(tarjeta) = TARJETA.get() {
140 // SEGURIDAD: en contexto de IRQ las interrupciones ya estan acalladas;
141 // tomar el cerrojo aqui no puede interbloquear con las tareas, que
142 // siempre lo toman con `interrupts::without_interrupts`.
143 let _ = tarjeta.lock().0.ack_interrupt();
144 }
145}
146
147/// La linea de IRQ del dispositivo, si el firmware enruto una util.
148pub fn irq() -> Option<u8> {
149 let v = IRQ_RED.load(Ordering::SeqCst);
150 if v == 0 {
151 None
152 } else {
153 Some(v)
154 }
155}
156
157// =============================================================================
158// Consulta y E/S — la interfaz para las tareas cooperativas
159// =============================================================================
160
161/// La MAC del dispositivo. `None` si la tarjeta aun no se ha montado.
162#[allow(dead_code)]
163pub fn mac() -> Option<Mac> {
164 MAC.get().copied()
165}
166
167/// Numero de paquetes recibidos desde el arranque.
168#[allow(dead_code)]
169pub fn paquetes_rx() -> u64 {
170 PAQUETES_RX.load(Ordering::Relaxed)
171}
172
173/// Numero de paquetes transmitidos desde el arranque.
174#[allow(dead_code)]
175pub fn paquetes_tx() -> u64 {
176 PAQUETES_TX.load(Ordering::Relaxed)
177}
178
179/// Envia un frame Ethernet crudo (cabecera + payload, sin CRC — el dispositivo
180/// se la añade). El llamante construye el frame entero.
181pub fn enviar(frame: &[u8]) -> Result<(), &'static str> {
182 let tarjeta = TARJETA.get().ok_or("red no montada")?;
183 interrupts::without_interrupts(|| {
184 let mut tarjeta = tarjeta.lock();
185 let mut tx = tarjeta.0.new_tx_buffer(frame.len());
186 tx.packet_mut().copy_from_slice(frame);
187 tarjeta.0.send(tx).map_err(|_| "envio fallido")?;
188 PAQUETES_TX.fetch_add(1, Ordering::Relaxed);
189 Ok(())
190 })
191}
192
193/// Drena los paquetes RX pendientes y aplica `callback` a cada uno. Cada
194/// bufer se recicla a la cola RX al terminar — el dispositivo tiene siempre
195/// receptores listos para la proxima IRQ.
196#[allow(dead_code)]
197pub fn drenar_rx<F: FnMut(&[u8])>(mut callback: F) {
198 let Some(tarjeta) = TARJETA.get() else {
199 return;
200 };
201 interrupts::without_interrupts(|| {
202 let mut tarjeta = tarjeta.lock();
203 loop {
204 if !tarjeta.0.can_recv() {
205 break;
206 }
207 let rx = match tarjeta.0.receive() {
208 Ok(r) => r,
209 Err(_) => break,
210 };
211 callback(rx.packet());
212 let _ = tarjeta.0.recycle_rx_buffer(rx);
213 PAQUETES_RX.fetch_add(1, Ordering::Relaxed);
214 }
215 });
216}
217
218/// Recibe UN paquete: copia su contenido en `buf` y devuelve los bytes
219/// copiados, o `0` si no hay paquete pendiente. La interfaz que el host
220/// expone a los apps via `sys_net_recibir` — un paquete por llamada (Fase 19).
221pub fn recibir_en(buf: &mut [u8]) -> usize {
222 let Some(tarjeta) = TARJETA.get() else {
223 return 0;
224 };
225 interrupts::without_interrupts(|| {
226 let mut tarjeta = tarjeta.lock();
227 if !tarjeta.0.can_recv() {
228 return 0;
229 }
230 let rx = match tarjeta.0.receive() {
231 Ok(r) => r,
232 Err(_) => return 0,
233 };
234 let pkt = rx.packet();
235 let n = pkt.len().min(buf.len());
236 buf[..n].copy_from_slice(&pkt[..n]);
237 let _ = tarjeta.0.recycle_rx_buffer(rx);
238 PAQUETES_RX.fetch_add(1, Ordering::Relaxed);
239 n
240 })
241}
242
243// =============================================================================
244// Composicion de un ARP request — el primer paquete que renaser saluda
245// =============================================================================
246
247/// Compone un frame Ethernet con una peticion ARP que pregunta por la MAC del
248/// host `objetivo_ip`. El gateway de QEMU lo responde — su replica entra por
249/// la cola RX y se registra en COM1 desde la tarea cooperativa de la red.
250pub fn componer_arp_request(
251 nuestro_mac: Mac,
252 nuestro_ip: [u8; 4],
253 objetivo_ip: [u8; 4],
254) -> [u8; 42] {
255 let mut frame = [0u8; 42];
256 // Cabecera Ethernet.
257 frame[0..6].copy_from_slice(&[0xff; 6]); // destino: broadcast
258 frame[6..12].copy_from_slice(&nuestro_mac);
259 frame[12..14].copy_from_slice(ÐER_TYPE_ARP.to_be_bytes());
260 // Payload ARP (28 bytes).
261 frame[14..16].copy_from_slice(&1u16.to_be_bytes()); // HW type: Ethernet
262 frame[16..18].copy_from_slice(&0x0800u16.to_be_bytes()); // proto: IPv4
263 frame[18] = 6; // HW len
264 frame[19] = 4; // proto len
265 frame[20..22].copy_from_slice(&1u16.to_be_bytes()); // opcode: REQUEST
266 frame[22..28].copy_from_slice(&nuestro_mac); // sender MAC
267 frame[28..32].copy_from_slice(&nuestro_ip); // sender IP
268 // bytes 32..38: target MAC, se quedan a cero
269 frame[38..42].copy_from_slice(&objetivo_ip); // target IP
270 frame
271}