gioser

tawasuyu/brahman

Rust 563TOML 212Markdown 66JSON 198014 Definiciones

⎇ 8fc1d99ddff618eaa0c2c29f4a51eaaed73443dd renaser/kernel/src/pic.rs Rust · 147 líneas · 6.0 KB Historia Quién tocó qué crudo

feat: integra renaser (kernel SASOS bare-metal) al monorepo e2272c0ed · sergio ·

1// =============================================================================
2//  renaser :: kernel/src/pic.rs — Fase 2.1 :: el par 8259 y el metronomo
3// -----------------------------------------------------------------------------
4//  El controlador de interrupciones 8259 (PIC) nace, por herencia historica,
5//  con sus vectores solapados sobre los de las excepciones de CPU. Antes de
6//  encender las interrupciones HAY que remapearlo: desplazar sus vectores
7//  fuera del rango 0..31. Aqui tambien programamos el PIT, el temporizador
8//  que dara a renaser su latido regular — el origen de su fluidez.
9// =============================================================================
10
11use x86_64::instructions::port::Port;
12
13// --- Puertos del par de 8259 (maestro + esclavo en cascada). ---
14const CMD_MAESTRO: u16 = 0x20;
15const DATOS_MAESTRO: u16 = 0x21;
16const CMD_ESCLAVO: u16 = 0xA0;
17const DATOS_ESCLAVO: u16 = 0xA1;
18
19// --- Puertos del temporizador de intervalos programable (PIT). ---
20const PIT_COMANDO: u16 = 0x43;
21const PIT_CANAL0: u16 = 0x40;
22/// Frecuencia base del cristal del PIT, en Hz.
23const PIT_BASE_HZ: u32 = 1_193_182;
24
25/// Orden de «fin de interrupcion» que el PIC espera tras cada IRQ atendida.
26const EOI: u8 = 0x20;
27
28/// Vector base del PIC maestro tras el remapeo (justo encima de las 32
29/// excepciones reservadas de la arquitectura).
30const OFFSET_MAESTRO: u8 = 0x20;
31/// Vector base del PIC esclavo tras el remapeo.
32const OFFSET_ESCLAVO: u8 = 0x28;
33
34/// Vector de la IRQ0 — el temporizador (PIT).
35pub const VECTOR_TEMPORIZADOR: u8 = OFFSET_MAESTRO; // 0x20
36/// Vector de la IRQ1 — el teclado.
37pub const VECTOR_TECLADO: u8 = OFFSET_MAESTRO + 1; // 0x21
38
39/// Remapea el par 8259 y programa el PIT a 100 Hz.
40///
41/// Debe invocarse una sola vez, tras cargar la IDT y ANTES de habilitar las
42/// interrupciones con `sti`.
43pub fn init() {
44    remapear();
45    configurar_temporizador(100);
46}
47
48/// Reprograma los vectores del 8259 fuera del rango de las excepciones de CPU.
49fn remapear() {
50    // SEGURIDAD: estos son los puertos de E/S estandar del 8259 en la
51    // arquitectura PC; la secuencia ICW1..ICW4 es su protocolo de inicio.
52    unsafe {
53        let mut cmd_m = Port::<u8>::new(CMD_MAESTRO);
54        let mut dat_m = Port::<u8>::new(DATOS_MAESTRO);
55        let mut cmd_e = Port::<u8>::new(CMD_ESCLAVO);
56        let mut dat_e = Port::<u8>::new(DATOS_ESCLAVO);
57        // Escribir en un puerto inerte da al 8259 el respiro que necesita
58        // entre palabras de control en hardware antiguo.
59        let mut respiro = Port::<u8>::new(0x80);
60
61        // ICW1 — iniciar la secuencia: en cascada, con ICW4 presente.
62        cmd_m.write(0x11u8);
63        respiro.write(0u8);
64        cmd_e.write(0x11u8);
65        respiro.write(0u8);
66        // ICW2 — el remapeo en si: desplazar los vectores lejos de 0..31.
67        dat_m.write(OFFSET_MAESTRO);
68        respiro.write(0u8);
69        dat_e.write(OFFSET_ESCLAVO);
70        respiro.write(0u8);
71        // ICW3 — declarar el cableado de la cascada (el esclavo en la IRQ2).
72        dat_m.write(0b0000_0100u8);
73        respiro.write(0u8);
74        dat_e.write(0b0000_0010u8);
75        respiro.write(0u8);
76        // ICW4 — modo 8086.
77        dat_m.write(0x01u8);
78        respiro.write(0u8);
79        dat_e.write(0x01u8);
80        respiro.write(0u8);
81        // Mascaras: el maestro deja pasar la IRQ0 (temporizador) y la IRQ1
82        // (teclado); todo lo demas, en silencio hasta que renaser lo reclame.
83        dat_m.write(0b1111_1100u8);
84        dat_e.write(0b1111_1111u8);
85    }
86}
87
88/// Programa el canal 0 del PIT para que emita la IRQ0 a `frecuencia_hz`.
89fn configurar_temporizador(frecuencia_hz: u32) {
90    let divisor = (PIT_BASE_HZ / frecuencia_hz) as u16;
91    // SEGURIDAD: 0x43 y 0x40 son los puertos estandar del PIT en el PC.
92    unsafe {
93        let mut comando = Port::<u8>::new(PIT_COMANDO);
94        let mut canal0 = Port::<u8>::new(PIT_CANAL0);
95        // Canal 0, acceso lobyte+hibyte, modo 3 (generador de onda cuadrada).
96        comando.write(0x36u8);
97        canal0.write((divisor & 0xFF) as u8);
98        canal0.write((divisor >> 8) as u8);
99    }
100}
101
102/// Notifica al PIC el «fin de interrupcion» de la IRQ recien atendida. Sin
103/// este aviso, el PIC jamas volveria a emitir esa interrupcion.
104pub fn fin_de_interrupcion(vector: u8) {
105    // SEGURIDAD: enviar EOI al puerto de comandos del PIC tras atender su IRQ
106    // es el cierre obligatorio del protocolo del 8259.
107    unsafe {
108        // Si la IRQ provino del esclavo, ambos PIC deben recibir el EOI.
109        if vector >= OFFSET_ESCLAVO {
110            Port::<u8>::new(CMD_ESCLAVO).write(EOI);
111        }
112        Port::<u8>::new(CMD_MAESTRO).write(EOI);
113    }
114}
115
116/// Vector de la IDT que corresponde a una linea de IRQ (0..15). El remapeo dejo
117/// las 16 lineas del par 8259 en vectores contiguos desde `OFFSET_MAESTRO`: la
118/// IRQ8 cae en `OFFSET_ESCLAVO`, que es justo `OFFSET_MAESTRO + 8`.
119pub fn vector_irq(irq: u8) -> u8 {
120    OFFSET_MAESTRO + irq
121}
122
123/// Levanta la mascara de una linea de IRQ — el PIC empezara a emitirla. Si la
124/// linea vive en el PIC esclavo (8..15), abre tambien la cascada del maestro
125/// (la IRQ2), sin la cual el esclavo jamas alcanzaria a la CPU.
126///
127/// Debe invocarse en el arranque, antes de habilitar las interrupciones.
128pub fn desenmascarar(irq: u8) {
129    // SEGURIDAD: 0x21 y 0xA1 son los puertos de mascara del par 8259 en la
130    // arquitectura PC; leer-modificar-escribir es la via de tocar una linea
131    // sola sin perturbar a las demas.
132    unsafe {
133        if irq < 8 {
134            let mut datos = Port::<u8>::new(DATOS_MAESTRO);
135            let mascara = datos.read();
136            datos.write(mascara & !(1 << irq));
137        } else {
138            let mut datos_e = Port::<u8>::new(DATOS_ESCLAVO);
139            let mascara_e = datos_e.read();
140            datos_e.write(mascara_e & !(1 << (irq - 8)));
141            // La cascada: el esclavo entrega sus IRQ al maestro por la IRQ2.
142            let mut datos_m = Port::<u8>::new(DATOS_MAESTRO);
143            let mascara_m = datos_m.read();
144            datos_m.write(mascara_m & !(1 << 2));
145        }
146    }
147}