feat(renaser): Fase 18 — red: virtio-net y el primer hola al exterior
1// =============================================================================
2// renaser :: kernel/src/interrupts.rs — Fase 2/3 :: la IDT y los reflejos
3// -----------------------------------------------------------------------------
4// La Interrupt Descriptor Table es la tabla de reflejos del procesador. ante
5// cada excepcion o IRQ, la CPU abandona lo que hacia y salta a la entrada que
6// le corresponde. renaser distingue tres naturalezas de impulso:
7//
8// * RECUPERABLE — el breakpoint (#BP): se atiende y la ejecucion prosigue.
9// * FATAL — el resto de excepciones: se entregan al `panic!`.
10// * HARDWARE — temporizador, teclado y disco: ya NO escriben estado
11// rustico, sino que alimentan el reactor asincrono.
12// =============================================================================
13
14use core::sync::atomic::{AtomicU8, Ordering};
15
16use x86_64::structures::idt::{InterruptDescriptorTable, InterruptStackFrame, PageFaultErrorCode};
17
18use crate::{gdt, pic, CeldaSync};
19
20/// La Interrupt Descriptor Table propia de renaser.
21static IDT: CeldaSync<InterruptDescriptorTable> =
22 CeldaSync::nueva(InterruptDescriptorTable::new());
23
24/// Vector de la IDT asignado a la IRQ del disco. Vale 0 hasta que el montaje
25/// del disco (Fase 6.2) descubra su linea de IRQ y la registre — ninguna IRQ
26/// legitima del disco vive en el vector 0 (reservado a las excepciones).
27static VECTOR_DISCO: AtomicU8 = AtomicU8::new(0);
28
29/// Vector de la IDT asignado a la IRQ de la red (Fase 18). Mismo patron.
30static VECTOR_RED: AtomicU8 = AtomicU8::new(0);
31
32/// Construye y activa la Interrupt Descriptor Table.
33///
34/// Debe invocarse una sola vez, durante el arranque, DESPUES de [`gdt::init`].
35pub fn init() {
36 // SEGURIDAD: `init` corre una sola vez, en arranque secuencial de un solo
37 // hilo; nada mas referencia la IDT todavia.
38 let idt: &'static mut InterruptDescriptorTable = unsafe { &mut *IDT.puntero() };
39
40 // --- Excepciones de CPU ---
41 idt.breakpoint.set_handler_fn(reflejo_breakpoint);
42 idt.invalid_opcode.set_handler_fn(reflejo_opcode_invalido);
43 idt.divide_error.set_handler_fn(reflejo_division);
44 idt.general_protection_fault
45 .set_handler_fn(reflejo_proteccion_general);
46 idt.page_fault.set_handler_fn(reflejo_fallo_pagina);
47
48 // El doble fallo se atiende SIEMPRE sobre el stack de emergencia del TSS:
49 // ni un desbordamiento de la pila del kernel impedira su diagnostico.
50 // SEGURIDAD: el indice IST referido fue armado previamente por `gdt::init`.
51 unsafe {
52 idt.double_fault
53 .set_handler_fn(reflejo_doble_fallo)
54 .set_stack_index(gdt::IST_DOBLE_FALLO);
55 }
56
57 // --- Interrupciones de hardware, ya remapeadas por el PIC ---
58 idt[pic::VECTOR_TEMPORIZADOR].set_handler_fn(irq_temporizador);
59 idt[pic::VECTOR_TECLADO].set_handler_fn(irq_teclado);
60 idt[pic::vector_irq(12)].set_handler_fn(irq_raton);
61
62 let idt_estatica: &'static InterruptDescriptorTable = idt;
63 idt_estatica.load();
64}
65
66/// Registra el manejador de la IRQ del disco virtio-blk en la entrada de la IDT
67/// que corresponde a `irq` (Fase 6.2). Lo invoca el montaje del disco, una vez
68/// descubierta la linea de IRQ que el firmware le asigno.
69///
70/// Se llama durante el arranque secuencial, con las interrupciones aun
71/// desactivadas y la linea todavia enmascarada en el PIC: la entrada que se
72/// escribe no puede dispararse mientras se escribe.
73pub fn registrar_irq_disco(irq: u8) {
74 let vector = pic::vector_irq(irq);
75 VECTOR_DISCO.store(vector, Ordering::SeqCst);
76 // SEGURIDAD: el arranque es secuencial y de un solo hilo; las interrupciones
77 // estan desactivadas y la linea del disco, enmascarada. La IDT ya esta
78 // cargada (`init` hizo `lidt`), pero la CPU relee cada entrada en cada
79 // interrupcion: modificar esta entrada en memoria surte efecto de inmediato.
80 let idt: &'static mut InterruptDescriptorTable = unsafe { &mut *IDT.puntero() };
81 idt[vector].set_handler_fn(irq_disco);
82}
83
84/// Registra el manejador de la IRQ de la red virtio-net en la IDT (Fase 18).
85/// Gemelo de [`registrar_irq_disco`]: las mismas condiciones de arranque
86/// secuencial garantizan la mutacion segura.
87pub fn registrar_irq_red(irq: u8) {
88 let vector = pic::vector_irq(irq);
89 VECTOR_RED.store(vector, Ordering::SeqCst);
90 // SEGURIDAD: ver `registrar_irq_disco`.
91 let idt: &'static mut InterruptDescriptorTable = unsafe { &mut *IDT.puntero() };
92 idt[vector].set_handler_fn(irq_red);
93}
94
95// =============================================================================
96// REFLEJOS DE EXCEPCION — las rutinas a las que la CPU salta ante cada fallo
97// =============================================================================
98
99/// #BP — Breakpoint. Excepcion RECUPERABLE: se atiende sin ruido y, al
100/// retornar, la CPU reanuda la ejecucion justo tras la instruccion `int3`.
101extern "x86-interrupt" fn reflejo_breakpoint(_marco: InterruptStackFrame) {}
102
103/// #UD — Opcode invalido. Fatal: la corriente de instrucciones es incoherente.
104extern "x86-interrupt" fn reflejo_opcode_invalido(marco: InterruptStackFrame) {
105 panic!("EXCEPCION FATAL :: opcode invalido (#UD)\n{marco:#?}");
106}
107
108/// #DE — Error de division. Fatal.
109extern "x86-interrupt" fn reflejo_division(marco: InterruptStackFrame) {
110 panic!("EXCEPCION FATAL :: error de division (#DE)\n{marco:#?}");
111}
112
113/// #GP — Fallo de proteccion general. Fatal.
114extern "x86-interrupt" fn reflejo_proteccion_general(marco: InterruptStackFrame, codigo: u64) {
115 panic!("EXCEPCION FATAL :: proteccion general (#GP) codigo={codigo:#x}\n{marco:#?}");
116}
117
118/// #PF — Fallo de pagina. Fatal en esta fase (sin memoria virtual dinamica).
119extern "x86-interrupt" fn reflejo_fallo_pagina(
120 marco: InterruptStackFrame,
121 codigo: PageFaultErrorCode,
122) {
123 let direccion = x86_64::registers::control::Cr2::read_raw();
124 panic!("EXCEPCION FATAL :: fallo de pagina (#PF) en {direccion:#x} {codigo:?}\n{marco:#?}");
125}
126
127/// #DF — Doble fallo. Fatal e irreversible: por definicion, diverge. Se ejecuta
128/// sobre el stack de emergencia del TSS, nunca sobre la pila comprometida.
129extern "x86-interrupt" fn reflejo_doble_fallo(marco: InterruptStackFrame, _codigo: u64) -> ! {
130 panic!("EXCEPCION FATAL :: doble fallo (#DF)\n{marco:#?}");
131}
132
133// =============================================================================
134// IMPULSOS DE HARDWARE — productores de eventos para el reactor asincrono
135// =============================================================================
136
137/// IRQ0 — Temporizador. Desde la Fase 5 marca el compas del userspace: cada
138/// pulso avanza el `reloj` y despierta a las tareas que aguardaban su fotograma.
139extern "x86-interrupt" fn irq_temporizador(_marco: InterruptStackFrame) {
140 crate::async_system::reloj::pulso();
141 pic::fin_de_interrupcion(pic::VECTOR_TEMPORIZADOR);
142}
143
144/// IRQ1 — Teclado. Ya no escribe estado rustico: lee el scancode crudo y lo
145/// entrega al reactor asincrono, que despertara a la tarea del teclado.
146extern "x86-interrupt" fn irq_teclado(_marco: InterruptStackFrame) {
147 // SEGURIDAD: 0x60 es el puerto de datos del controlador PS/2, fijo en la
148 // arquitectura PC. Leerlo, ademas, libera al controlador para el siguiente.
149 let scancode: u8 = unsafe { x86_64::instructions::port::Port::new(0x60).read() };
150 crate::async_system::teclado::recibir_scancode(scancode);
151 pic::fin_de_interrupcion(pic::VECTOR_TECLADO);
152}
153
154/// IRQ12 — Raton PS/2 (Fase 13). Lee un byte del puerto de datos del 8042 —el
155/// mismo que sirve al teclado—; el ensamblador de paquetes del raton se ocupa
156/// de juntar tres bytes y entregar el evento al compositor.
157extern "x86-interrupt" fn irq_raton(_marco: InterruptStackFrame) {
158 // SEGURIDAD: 0x60 es el puerto de datos del 8042 en la arquitectura PC.
159 // Que la IRQ12 ha disparado garantiza que el byte es del raton, no del
160 // teclado: el controlador alza una linea distinta para cada dispositivo.
161 let byte: u8 = unsafe { x86_64::instructions::port::Port::new(0x60).read() };
162 crate::drivers::raton::recibir_byte(byte);
163 pic::fin_de_interrupcion(pic::vector_irq(12));
164}
165
166/// IRQ del disco — virtio-blk (Fase 6.2). El disco ha terminado una
167/// transferencia: `atender_irq` reconoce la interrupcion en el dispositivo
168/// —lo que libera su linea— y despierta a la tarea que aguardaba el bloque.
169/// Asi la E/S de disco deja de bloquear el planificador por sondeo.
170extern "x86-interrupt" fn irq_disco(_marco: InterruptStackFrame) {
171 crate::drivers::disco::atender_irq();
172 // El EOI se cierra DESPUES de reconocer al dispositivo: una IRQ legada de
173 // PCI es de nivel — anunciar el fin sin haber bajado la linea la reavivaria.
174 pic::fin_de_interrupcion(VECTOR_DISCO.load(Ordering::SeqCst));
175}
176
177/// IRQ de la red — virtio-net (Fase 18). Llego un paquete (o un envio termino):
178/// acknowledge en el dispositivo —que baja la linea— y EOI al PIC. Las tareas
179/// cooperativas drenan despues la cola RX y consumen los paquetes.
180extern "x86-interrupt" fn irq_red(_marco: InterruptStackFrame) {
181 crate::drivers::red::atender_irq();
182 pic::fin_de_interrupcion(VECTOR_RED.load(Ordering::SeqCst));
183}