hal_x86_64/interrupt/
pic.rs

1use super::IsaInterrupt;
2use crate::cpu;
3use hal_core::interrupt::{Handlers, RegistrationError};
4
5#[derive(Debug)]
6pub(crate) struct Pic {
7    address: u8,
8    command: cpu::Port,
9    data: cpu::Port,
10}
11
12impl Pic {
13    const fn new(address: u8, command: u16, data: u16) -> Self {
14        Self {
15            address,
16            command: cpu::Port::at(command),
17            data: cpu::Port::at(data),
18        }
19    }
20
21    /// Mask the provided interrupt number.
22    unsafe fn mask(&mut self, num: u8) {
23        debug_assert!(num < 8);
24        // read the current value of the Interrupt Mask Register (IMR).
25        let imr = self.data.readb();
26        // set the bit corresponding to the interrupt number to 1.
27        self.data.writeb(imr | (1 << num));
28    }
29
30    /// Unmask the provided interrupt number.
31    unsafe fn unmask(&mut self, num: u8) {
32        debug_assert!(num < 8);
33        // read the current value of the Interrupt Mask Register (IMR).
34        let imr = self.data.readb();
35        // clear the bit corresponding to the interrupt number.
36        self.data.writeb(imr & !(1 << num));
37    }
38}
39
40#[derive(Debug)]
41pub struct CascadedPic {
42    sisters: PicSisters,
43}
44
45// two of them
46
47#[derive(Debug)]
48struct PicSisters {
49    big: Pic,
50    little: Pic,
51}
52
53impl PicSisters {
54    pub(crate) const fn new() -> Self {
55        Self {
56            // primary and secondary PIC addresses (0 and 8) are Just How IBM Did It.
57            // yes, they overlap with x86 interrupt numbers. it's not good.
58            // port numbers are also magic IBM PC/AT architecture numbers.
59            big: Pic::new(0, 0x20, 0x21),
60            little: Pic::new(8, 0xa0, 0xa1),
61        }
62    }
63}
64
65impl CascadedPic {
66    pub(crate) const fn new() -> Self {
67        Self {
68            sisters: PicSisters::new(),
69        }
70    }
71
72    pub(crate) fn mask(&mut self, irq: IsaInterrupt) {
73        let (pic, num) = self.pic_for_irq(irq);
74        unsafe {
75            pic.mask(num);
76        }
77    }
78
79    pub(crate) fn unmask(&mut self, irq: IsaInterrupt) {
80        let (pic, num) = self.pic_for_irq(irq);
81        unsafe {
82            pic.unmask(num);
83        }
84    }
85
86    fn pic_for_irq(&mut self, irq: IsaInterrupt) -> (&mut Pic, u8) {
87        let num = irq as u8;
88        if num >= 8 {
89            (&mut self.sisters.little, num - 8)
90        } else {
91            (&mut self.sisters.big, num)
92        }
93    }
94
95    pub(crate) fn end_interrupt(&mut self, irq: IsaInterrupt) {
96        const END_INTERRUPT: u8 = 0x20; // from osdev wiki
97        let num = irq as u8;
98        if num >= 8 {
99            unsafe {
100                self.sisters.little.command.writeb(END_INTERRUPT);
101            }
102        }
103
104        unsafe {
105            self.sisters.big.command.writeb(END_INTERRUPT);
106        }
107    }
108}
109
110impl hal_core::interrupt::Control for CascadedPic {
111    type Registers = super::Registers;
112    fn register_handlers<H>(&mut self) -> Result<(), hal_core::interrupt::RegistrationError>
113    where
114        H: Handlers<super::Registers>,
115    {
116        Err(RegistrationError::other(
117            "x86_64 handlers must be registered via the IDT, not to the PIC interrupt component",
118        ))
119    }
120
121    unsafe fn disable(&mut self) {
122        self.sisters.big.data.writeb(0xff);
123        self.sisters.little.data.writeb(0xff);
124    }
125
126    unsafe fn enable(&mut self) {
127        // TODO(ixi): confirm this?? it looks like "disable" is "write a 1 to set the line masked"
128        //            so maybe it stands to reason that writing a 0 unmasks an interrupt?
129        self.sisters.big.data.writeb(0x00);
130        self.sisters.little.data.writeb(0x00);
131    }
132
133    fn is_enabled(&self) -> bool {
134        unimplemented!("ixi do this one!!!")
135    }
136}
137
138impl CascadedPic {
139    pub(crate) unsafe fn set_irq_address(&mut self, primary_start: u8, secondary_start: u8) {
140        // iowait and its uses below are guidance from the osdev wiki for compatibility with "older
141        // machines". it is not entirely clear what "older machines" exactly means, or where this
142        // is or is not necessary precisely. this code happens to work in qemu without `iowait()`,
143        // but is largely untested on real hardware where this may be a concern.
144        let iowait = || cpu::Port::at(0x80).writeb(0);
145
146        let primary_mask = self.sisters.big.data.readb();
147        let secondary_mask = self.sisters.little.data.readb();
148
149        const EXTENDED_CONFIG: u8 = 0x01; // if present, there are four initialization control words
150        const PIC_INIT: u8 = 0x10; // reinitialize the 8259 PIC
151
152        self.sisters.big.command.writeb(PIC_INIT | EXTENDED_CONFIG);
153        iowait();
154        self.sisters
155            .little
156            .command
157            .writeb(PIC_INIT | EXTENDED_CONFIG);
158        iowait();
159        self.sisters.big.data.writeb(primary_start);
160        iowait();
161        self.sisters.little.data.writeb(secondary_start);
162        iowait();
163        // magic number: secondary pic is at cascade identity 2 (how does 4 say this ???)
164        // !UPDATE! 4 says this because this word is a bitmask:
165        //
166        //   76543210
167        // 0b00000100
168        //        |
169        //        --- bit 2 set means there is another 8259 cascaded at address 2
170        //
171        // bit 0 would be set if there was only one controller in the system, in `single` mode, and
172        // is non-zero in all other modes. the exact read from the intel manual is not immediately
173        // clear:
174        // ```
175        // in sisters mode, a `1` is set for each little sister in the system.
176        // ```
177        // which means bit 1 indicates a little sister at cascade identity 1, or, as in an IBM
178        // PC/AT system, a little sister is present at cascade identity 2, indicated by bit 2 being
179        // set for a bitmask of 0b0000_0100. there is a slightly faded diagram that describes ICW3
180        // which has not been OCR'd, in the copy of the intel document you might find online.
181        self.sisters.big.data.writeb(4);
182        iowait();
183        // magic number: secondary pic has cascade identity 2 (??)
184        // !UPDATE! that's just how IBM PC/AT systems be
185        self.sisters.little.data.writeb(2);
186        iowait();
187        self.sisters.big.data.writeb(1); // 8086/88 (MCS-80/85) mode
188        iowait();
189        self.sisters.little.data.writeb(1); // 8086/88 (MCS-80/85) mode
190        iowait();
191
192        self.sisters.big.data.writeb(primary_mask);
193        iowait();
194        self.sisters.little.data.writeb(secondary_mask);
195        iowait();
196        self.sisters.big.address = primary_start;
197        self.sisters.little.address = secondary_start;
198    }
199}