Skip to main content

strat9_kernel/arch/x86_64/
pic.rs

1//! 8259 Programmable Interrupt Controller (PIC) driver
2//! Inspired by MaestroOS `pic.rs`
3//!
4//! The PIC handles hardware interrupts (IRQs) and maps them to
5//! CPU interrupt vectors. We remap IRQs to vectors 0x20-0x2F.
6
7use super::io::{inb, outb};
8
9/// Master PIC command port
10const MASTER_COMMAND: u16 = 0x20;
11/// Master PIC data port
12const MASTER_DATA: u16 = 0x21;
13/// Slave PIC command port
14const SLAVE_COMMAND: u16 = 0xA0;
15/// Slave PIC data port
16const SLAVE_DATA: u16 = 0xA1;
17
18/// ICW1: Initialization + ICW4 needed
19const ICW1_INIT: u8 = 0x10;
20const ICW1_ICW4: u8 = 0x01;
21/// ICW3: Slave PIC on IRQ2
22const ICW3_SLAVE_PIC: u8 = 0x04;
23/// ICW3: Cascade identity for slave
24const ICW3_CASCADE: u8 = 0x02;
25/// ICW4: 8086 mode
26const ICW4_8086: u8 = 0x01;
27
28/// End-of-interrupt command
29const COMMAND_EOI: u8 = 0x20;
30
31/// IRQ offset for master PIC (IRQ0 -> interrupt 0x20)
32pub const PIC1_OFFSET: u8 = 0x20;
33/// IRQ offset for slave PIC (IRQ8 -> interrupt 0x28)
34pub const PIC2_OFFSET: u8 = 0x28;
35
36/// Initialize the PIC with the given offsets.
37///
38/// Remaps IRQ0-7 to `offset1` and IRQ8-15 to `offset2`.
39pub fn init(offset1: u8, offset2: u8) {
40    unsafe {
41        // Save masks
42        let mask1 = inb(MASTER_DATA);
43        let mask2 = inb(SLAVE_DATA);
44
45        // Start initialization sequence
46        outb(MASTER_COMMAND, ICW1_INIT | ICW1_ICW4);
47        super::io::io_wait();
48        outb(SLAVE_COMMAND, ICW1_INIT | ICW1_ICW4);
49        super::io::io_wait();
50
51        // Set vector offsets
52        outb(MASTER_DATA, offset1);
53        super::io::io_wait();
54        outb(SLAVE_DATA, offset2);
55        super::io::io_wait();
56
57        // Configure cascading
58        outb(MASTER_DATA, ICW3_SLAVE_PIC);
59        super::io::io_wait();
60        outb(SLAVE_DATA, ICW3_CASCADE);
61        super::io::io_wait();
62
63        // Set 8086 mode
64        outb(MASTER_DATA, ICW4_8086);
65        super::io::io_wait();
66        outb(SLAVE_DATA, ICW4_8086);
67        super::io::io_wait();
68
69        // Restore saved masks
70        outb(MASTER_DATA, mask1);
71        outb(SLAVE_DATA, mask2);
72    }
73}
74
75/// Disable all IRQs on both PICs.
76pub fn disable() {
77    unsafe {
78        outb(MASTER_DATA, 0xFF);
79        outb(SLAVE_DATA, 0xFF);
80    }
81}
82
83/// Disable the 8259 PIC permanently by masking all IRQs.
84///
85/// Call this after remapping the PIC (to avoid stray interrupts at
86/// CPU exception vectors) and after the I/O APIC is ready to take over.
87pub fn disable_permanently() {
88    unsafe {
89        outb(MASTER_DATA, 0xFF);
90        outb(SLAVE_DATA, 0xFF);
91    }
92    log::info!("Legacy 8259 PIC disabled permanently");
93}
94
95/// Enable a specific IRQ line.
96pub fn enable_irq(mut irq: u8) {
97    let port = if irq < 8 {
98        MASTER_DATA
99    } else {
100        irq -= 8;
101        SLAVE_DATA
102    };
103    unsafe {
104        let value = inb(port) & !(1 << irq);
105        outb(port, value);
106    }
107}
108
109/// Disable a specific IRQ line.
110pub fn disable_irq(mut irq: u8) {
111    let port = if irq < 8 {
112        MASTER_DATA
113    } else {
114        irq -= 8;
115        SLAVE_DATA
116    };
117    unsafe {
118        let value = inb(port) | (1 << irq);
119        outb(port, value);
120    }
121}
122
123/// Send End-Of-Interrupt to the PIC for the given IRQ.
124pub fn end_of_interrupt(irq: u8) {
125    unsafe {
126        if irq >= 8 {
127            outb(SLAVE_COMMAND, COMMAND_EOI);
128        }
129        outb(MASTER_COMMAND, COMMAND_EOI);
130    }
131}