hal_x86_64/interrupt/
idt.rs

1use super::IsaInterrupt;
2use crate::{cpu, segment};
3use mycelium_util::{bits, fmt};
4
5#[repr(C)]
6#[repr(align(16))]
7pub struct Idt {
8    pub(crate) descriptors: [Descriptor; Self::NUM_VECTORS],
9}
10
11#[derive(Copy, Clone, Eq, PartialEq)]
12#[repr(C)]
13pub struct Descriptor {
14    offset_low: u16,
15    segment: segment::Selector,
16    ist_offset: u8,
17    attrs: Attrs,
18    offset_mid: u16,
19    offset_hi: u32,
20    _zero: u32,
21}
22
23mycelium_util::bits::bitfield! {
24    #[derive(Eq, PartialEq)]
25    pub struct Attrs<u8> {
26        pub const GATE_KIND: GateKind;
27        pub const IS_32_BIT: bool;
28        pub const RING: cpu::Ring;
29        const _PAD = 1;
30        pub const PRESENT: bool;
31    }
32}
33
34impl Descriptor {
35    pub const fn null() -> Self {
36        Self {
37            offset_low: 0,
38            segment: segment::Selector::null(),
39            ist_offset: 0,
40            attrs: Attrs::null(),
41            offset_mid: 0,
42            offset_hi: 0,
43            _zero: 0,
44        }
45    }
46
47    pub(crate) fn set_handler(&mut self, handler: *const ()) -> &mut Self {
48        self.segment = segment::Selector::current_cs();
49        let addr = handler as u64;
50        self.offset_low = addr as u16;
51        self.offset_mid = (addr >> 16) as u16;
52        self.offset_hi = (addr >> 32) as u32;
53        self.attrs
54            .set_present(true)
55            .set_32_bit(true)
56            .set_gate_kind(GateKind::Interrupt);
57        self
58    }
59
60    /// Sets the descriptor's [Interrupt Stack Table][ist] offset.
61    ///
62    /// [ist]: https://en.wikipedia.org/wiki/Task_state_segment#Inner-level_stack_pointers
63    pub(crate) fn set_ist_offset(&mut self, ist_offset: u8) -> &mut Self {
64        self.ist_offset = ist_offset;
65        self
66    }
67
68    /// Mutably borrows the descriptor's [attributes](Attrs).
69    #[inline]
70    #[must_use]
71    pub fn attrs_mut(&mut self) -> &mut Attrs {
72        &mut self.attrs
73    }
74}
75
76#[derive(Debug, Eq, PartialEq, Copy, Clone)]
77#[repr(u8)]
78pub enum GateKind {
79    Interrupt = 0b0000_0110,
80    Trap = 0b0000_0111,
81    Task = 0b0000_0101,
82}
83
84impl bits::FromBits<u8> for GateKind {
85    const BITS: u32 = 3;
86    type Error = &'static str;
87
88    fn try_from_bits(bits: u8) -> Result<Self, Self::Error> {
89        match bits {
90            bits if bits == Self::Interrupt as u8 => Ok(Self::Interrupt),
91            bits if bits == Self::Trap as u8 => Ok(Self::Trap),
92            bits if bits == Self::Task as u8 => Ok(Self::Task),
93            _ => Err("unknown GateKind pattern, expected one of [110, 111, or 101]"),
94        }
95    }
96
97    fn into_bits(self) -> u8 {
98        self as u8
99    }
100}
101
102// === impl Idt ===
103
104impl Idt {
105    pub const NUM_VECTORS: usize = 256;
106
107    /// Divide-by-zero interrupt (#D0)
108    pub const DIVIDE_BY_ZERO: usize = 0;
109
110    pub const DEBUG: usize = 1;
111
112    /// Non-maskable interrupt.
113    pub const NMI: usize = 2;
114
115    pub const BREAKPOINT: usize = 3;
116
117    pub const OVERFLOW: usize = 4;
118
119    pub const BOUND_RANGE_EXCEEDED: usize = 5;
120
121    pub const INVALID_OPCODE: usize = 6;
122
123    /// A device not available exception
124    pub const DEVICE_NOT_AVAILABLE: usize = 7;
125
126    // TODO(eliza): can we enforce that this diverges?
127    pub const DOUBLE_FAULT: usize = 8;
128
129    /// On modern CPUs, this interrupt is reserved; this error fires a general
130    /// protection fault instead.
131    pub const COPROCESSOR_SEGMENT_OVERRUN: usize = 9;
132
133    pub const INVALID_TSS: usize = 10;
134
135    pub const SEGMENT_NOT_PRESENT: usize = 11;
136
137    pub const STACK_SEGMENT_FAULT: usize = 12;
138
139    pub const GENERAL_PROTECTION_FAULT: usize = 13;
140
141    pub const PAGE_FAULT: usize = 14;
142
143    pub const X87_FPU_EXCEPTION: usize = 16;
144
145    pub const ALIGNMENT_CHECK: usize = 17;
146
147    pub const MACHINE_CHECK: usize = 18;
148
149    pub const SIMD_FLOATING_POINT: usize = 19;
150
151    pub const VIRTUALIZATION_EXCEPTION: usize = 20;
152
153    pub const SECURITY_EXCEPTION: usize = 30;
154
155    /// Chosen by fair die roll, guaranteed to be random.
156    pub const DOUBLE_FAULT_IST_OFFSET: usize = 4;
157
158    pub const MAX_CPU_EXCEPTION: usize = Self::SECURITY_EXCEPTION;
159
160    /// Base offset for ISA hardware interrupts.
161    pub const ISA_BASE: usize = 0x20;
162
163    /// Local APIC timer interrupt vector mapped by
164    /// [`Controller::enable_hardware_interrupts`].
165    ///
166    /// Systems which do not use that function to initialize the local APIC may
167    /// map this interrupt to a different IDT vector.
168    ///
169    /// [`Controller::enable_hardware_interrupts`]: super::Controller::enable_hardware_interrupts
170    pub const LOCAL_APIC_TIMER: usize = (Self::NUM_VECTORS - 2);
171
172    /// Local APIC spurious interrupt vector mapped by
173    /// [`Controller::enable_hardware_interrupts`].
174    ///
175    /// Systems which do not use that function to initialize the local APIC may
176    /// map this interrupt to a different IDT vector.
177    ///
178    /// [`Controller::enable_hardware_interrupts`]: super::Controller::enable_hardware_interrupts
179    pub const LOCAL_APIC_SPURIOUS: usize = (Self::NUM_VECTORS - 1);
180
181    /// Base of the primary PIC's interrupt vector region mapped by
182    /// [`Controller::enable_hardware_interrupts`].
183    ///
184    /// Systems which do not use that function to initialize the PICs may
185    /// map this interrupt to a different IDT vector.
186    ///
187    /// [`Controller::enable_hardware_interrupts`]: super::Controller::enable_hardware_interrupts
188    pub const PIC_BIG_START: usize = Self::ISA_BASE;
189
190    /// Base of the secondary PIC's interrupt vector region mapped by
191    /// [`Controller::enable_hardware_interrupts`].
192    ///
193    /// Systems which do not use that function to initialize the PICs may
194    /// map this interrupt to a different IDT vector.
195    ///
196    /// [`Controller::enable_hardware_interrupts`]: super::Controller::enable_hardware_interrupts
197    pub const PIC_LITTLE_START: usize = Self::ISA_BASE + 8;
198    // put the IOAPIC right after the PICs
199
200    pub const fn new() -> Self {
201        Self {
202            descriptors: [Descriptor::null(); Self::NUM_VECTORS],
203        }
204    }
205
206    /// Register an interrupt service routine (ISR) for the given ISA standard
207    /// PC interrupt.
208    pub fn register_isa_isr(&mut self, irq: IsaInterrupt, isr: *const ()) {
209        let vector = irq as usize + Self::ISA_BASE;
210        self.register_isr(vector, isr);
211    }
212
213    pub fn register_isr(&mut self, vector: usize, isr: *const ()) {
214        let descr = self.descriptors[vector].set_handler(isr);
215        if vector == Self::DOUBLE_FAULT {
216            descr.set_ist_offset(Self::DOUBLE_FAULT_IST_OFFSET as u8);
217        }
218        tracing::debug!(vector, ?isr, ?descr, "set ISR");
219    }
220
221    pub fn load(&'static self) {
222        unsafe {
223            // Safety: the `'static` bound ensures the IDT isn't going away
224            // unless you did something really evil.
225            self.load_raw()
226        }
227    }
228
229    /// # Safety
230    ///
231    /// The referenced IDT must be valid for the `'static` lifetime.
232    pub unsafe fn load_raw(&self) {
233        let ptr = cpu::DtablePtr::new_unchecked(self);
234        tracing::debug!(?ptr, "loading IDT");
235        cpu::intrinsics::lidt(ptr);
236        tracing::debug!("IDT loaded!");
237    }
238}
239
240impl Default for Idt {
241    fn default() -> Self {
242        Self::new()
243    }
244}
245
246impl fmt::Debug for Idt {
247    fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
248        let Self { descriptors } = self;
249        let descriptors = descriptors[..]
250            .iter()
251            .filter(|&descr| descr != &Descriptor::null())
252            .enumerate()
253            .map(|(i, descr)| (fmt::hex(i), descr));
254        f.debug_map().entries(descriptors).finish()
255    }
256}
257
258// === impl Descriptor ===
259
260impl fmt::Debug for Descriptor {
261    fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
262        let Self {
263            offset_low,
264            segment,
265            ist_offset,
266            attrs,
267            offset_mid,
268            offset_hi,
269            _zero: _,
270        } = self;
271        f.debug_struct("Descriptor")
272            .field("offset_low", &format_args!("{offset_low:#x}"))
273            .field("segment", segment)
274            .field("ist_offset", &format_args!("{ist_offset:#x}"))
275            .field("attrs", attrs)
276            .field("offset_mid", &format_args!("{offset_mid:#x}"))
277            .field("offset_high", &format_args!("{offset_hi:#x}"))
278            .finish()
279    }
280}
281
282// === impl Attrs ===
283
284impl Attrs {
285    pub const fn null() -> Self {
286        Self(0)
287    }
288
289    pub fn gate_kind(&self) -> GateKind {
290        self.get(Self::GATE_KIND)
291    }
292
293    pub fn is_32_bit(&self) -> bool {
294        self.get(Self::IS_32_BIT)
295    }
296
297    pub fn is_present(&self) -> bool {
298        self.get(Self::PRESENT)
299    }
300
301    pub fn ring(&self) -> cpu::Ring {
302        self.get(Self::RING)
303    }
304
305    pub fn set_gate_kind(&mut self, kind: GateKind) -> &mut Self {
306        self.set(Self::GATE_KIND, kind)
307    }
308
309    pub fn set_32_bit(&mut self, is_32_bit: bool) -> &mut Self {
310        self.set(Self::IS_32_BIT, is_32_bit)
311    }
312
313    pub fn set_present(&mut self, present: bool) -> &mut Self {
314        self.set(Self::PRESENT, present)
315    }
316
317    pub fn set_ring(&mut self, ring: cpu::Ring) -> &mut Self {
318        self.set(Self::RING, ring)
319    }
320}
321
322#[cfg(test)]
323mod tests {
324    use super::*;
325
326    #[test]
327    fn idt_entry_is_correct_size() {
328        use core::mem::size_of;
329        assert_eq!(size_of::<Descriptor>(), 16);
330    }
331
332    #[test]
333    fn attrs_pack_specs() {
334        Attrs::assert_valid()
335    }
336
337    #[test]
338    fn idt_attrs_are_correct() {
339        let mut present_32bit_interrupt = Attrs::null();
340        present_32bit_interrupt
341            .set_present(true)
342            .set_32_bit(true)
343            .set_gate_kind(GateKind::Interrupt);
344        println!("{present_32bit_interrupt}");
345        // expected bit pattern here is:
346        // |   1|   0    0|   0|   1    1    1    0|
347        // |   P|      DPL|   Z|          Gate Type|
348        //
349        // P: Present (1)
350        // DPL: Descriptor Privilege Level (0 => ring 0)
351        // Z: this bit is 0 for a 64-bit IDT. for a 32-bit IDT, this may be 1 for task gates.
352        // Gate Type: 32-bit interrupt gate is 0b1110. that's just how it is.
353        assert_eq!(
354            present_32bit_interrupt.0, 0b1000_1110,
355            "\n attrs: {present_32bit_interrupt:#?}",
356        );
357    }
358
359    #[test]
360    fn idt_entry_is_correct() {
361        let mut idt_entry = Descriptor::null();
362        idt_entry.set_handler(0x1234_8765_abcd_fdec as *const ());
363
364        let idt_bytes = unsafe { core::mem::transmute::<&Descriptor, &[u8; 16]>(&idt_entry) };
365
366        let expected_idt: [u8; 16] = [
367            0xec, 0xfd, // offset bits 0..15 (little-endian? oh god)
368            0x33, 0x00, // selector (.. wait, this is from the host...)
369            0x00, // ist (no stack switching at the moment)
370            0x8e, // type/attr bits, 0x8e for 32-bit ring-0 interrupt descriptor
371            0xcd, 0xab, // bits 16..31 (still little-endian)
372            0x65, 0x87, 0x34, 0x12, // and bits 32..63
373            0x00, 0x00, 0x00, 0x00,
374        ];
375
376        assert_eq!(idt_bytes, &expected_idt, "\n entry: {idt_entry:#?}",);
377    }
378}