hal_x86_64/
interrupt.rs

1use crate::{cpu, mm, segment, time, VAddr};
2use core::{arch::asm, marker::PhantomData, time::Duration};
3use hal_core::interrupt::Control;
4use hal_core::interrupt::{ctx, Handlers};
5use hal_core::mem::page;
6use mycelium_util::{
7    bits, fmt,
8    sync::{
9        blocking::{Mutex, MutexGuard},
10        spin::Spinlock,
11        InitOnce,
12    },
13};
14
15pub mod apic;
16pub mod idt;
17pub mod pic;
18
19use self::apic::{IoApicSet, LocalApic};
20pub use idt::Idt;
21pub use pic::CascadedPic;
22
23#[derive(Debug)]
24pub struct Controller {
25    model: InterruptModel,
26}
27
28#[derive(Debug)]
29#[repr(C)]
30pub struct Context<'a, T = ()> {
31    registers: &'a mut Registers,
32    code: T,
33}
34
35pub type ErrorCode = u64;
36
37pub struct CodeFault<'a> {
38    kind: &'static str,
39    error_code: Option<&'a dyn fmt::Display>,
40}
41
42/// An interrupt service routine.
43pub type Isr<T> = extern "x86-interrupt" fn(&mut Context<T>);
44
45#[derive(Debug, thiserror::Error)]
46pub enum PeriodicTimerError {
47    #[error("could not start PIT periodic timer: {0}")]
48    Pit(#[from] time::PitError),
49    #[error(transparent)]
50    InvalidDuration(#[from] time::InvalidDuration),
51    #[error("could access local APIC: {0}")]
52    Apic(#[from] apic::local::LocalApicError),
53    #[error("could not start local APIC periodic timer: {0}")]
54    ApicTimer(#[from] apic::local::TimerError),
55}
56
57#[derive(Debug)]
58#[repr(C)]
59pub struct Interrupt<T = ()> {
60    vector: u8,
61    _t: PhantomData<T>,
62}
63
64/// The interrupt controller's active interrupt model.
65#[derive(Debug)]
66
67enum InterruptModel {
68    /// Interrupts are handled by the [8259 Programmable Interrupt Controller
69    /// (PIC)](pic).
70    Pic(Mutex<pic::CascadedPic, Spinlock>),
71    /// Interrupts are handled by the [local] and [I/O] [Advanced Programmable
72    /// Interrupt Controller (APIC)s][apics].
73    ///
74    /// [local]: apic::LocalApic
75    /// [I/O]: apic::IoApic
76    /// [apics]: apic
77    Apic {
78        io: apic::IoApicSet,
79        local: apic::local::Handle,
80    },
81}
82
83bits::bitfield! {
84    pub struct PageFaultCode<u32> {
85        /// When set, the page fault was caused by a page-protection violation.
86        /// When not set, it was caused by a non-present page.
87        pub const PRESENT: bool;
88        /// When set, the page fault was caused by a write access. When not set,
89        /// it was caused by a read access.
90        pub const WRITE: bool;
91        /// When set, the page fault was caused while CPL = 3. This does not
92        /// necessarily mean that the page fault was a privilege violation.
93        pub const USER: bool;
94        /// When set, one or more page directory entries contain reserved bits
95        /// which are set to 1. This only applies when the PSE or PAE flags in
96        /// CR4 are set to 1.
97        pub const RESERVED_WRITE: bool;
98        /// When set, the page fault was caused by an instruction fetch. This
99        /// only applies when the No-Execute bit is supported and enabled.
100        pub const INSTRUCTION_FETCH: bool;
101        /// When set, the page fault was caused by a protection-key violation.
102        /// The PKRU register (for user-mode accesses) or PKRS MSR (for
103        /// supervisor-mode accesses) specifies the protection key rights.
104        pub const PROTECTION_KEY: bool;
105        /// When set, the page fault was caused by a shadow stack access.
106        pub const SHADOW_STACK: bool;
107        const _RESERVED0 = 8;
108        /// When set, the fault was due to an SGX violation. The fault is
109        /// unrelated to ordinary paging.
110        pub const SGX: bool;
111    }
112}
113
114bits::bitfield! {
115    /// Error code set by the "Invalid TSS", "Segment Not Present", "Stack-Segment
116    /// Fault", and "General Protection Fault" faults.
117    ///
118    /// This includes a segment selector index, and includes 2 bits describing
119    /// which table the segment selector references.
120    pub struct SelectorErrorCode<u16> {
121        const EXTERNAL: bool;
122        const TABLE: cpu::DescriptorTable;
123        const INDEX = 13;
124    }
125}
126
127#[repr(C)]
128pub struct Registers {
129    pub instruction_ptr: VAddr, // TODO(eliza): add VAddr
130    pub code_segment: segment::Selector,
131    _pad: [u16; 3],
132    pub cpu_flags: u64,   // TODO(eliza): rflags type?
133    pub stack_ptr: VAddr, // TODO(eliza): add VAddr
134    pub stack_segment: segment::Selector,
135    _pad2: [u16; 3],
136}
137
138static IDT: Mutex<idt::Idt, Spinlock> = Mutex::new_with_raw_mutex(idt::Idt::new(), Spinlock::new());
139static INTERRUPT_CONTROLLER: InitOnce<Controller> = InitOnce::uninitialized();
140
141/// ISA interrupt vectors
142///
143/// See: [the other wiki](https://wiki.osdev.org/Interrupts#General_IBM-PC_Compatible_Interrupt_Information)
144#[derive(Copy, Clone, Debug)]
145#[repr(u8)]
146pub enum IsaInterrupt {
147    /// Programmable Interval Timer (PIT) timer interrupt.
148    PitTimer = 0,
149    /// PS/2 keyboard controller interrupt.
150    Ps2Keyboard = 1,
151    // IRQ 2 is reserved for the PIC cascade interrupt and isn't user accessible!
152    /// COM2 / COM4 serial port interrupt.
153    Com2 = 3,
154    /// COM1 / COM3 serial port interrupt.
155    Com1 = 4,
156    /// LPT2 parallel port interrupt.
157    Lpt2 = 5,
158    /// Floppy disk
159    Floppy = 6,
160    /// LPT1 parallel port interrupt, or spurious.
161    Lpt1 = 7,
162    /// CMOS real-time clock.
163    CmosRtc = 8,
164    /// Free for peripherals/SCSI/NIC
165    Periph1 = 9,
166    Periph2 = 10,
167    Periph3 = 11,
168    /// PS/2 Mouse
169    Ps2Mouse = 12,
170    /// FPU
171    Fpu = 13,
172    /// Primary ATA hard disk
173    AtaPrimary = 14,
174    /// Secondary ATA hard disk
175    AtaSecondary = 15,
176}
177
178impl IsaInterrupt {
179    pub const ALL: [IsaInterrupt; 15] = [
180        IsaInterrupt::PitTimer,
181        IsaInterrupt::Ps2Keyboard,
182        IsaInterrupt::Com2,
183        IsaInterrupt::Com1,
184        IsaInterrupt::Lpt2,
185        IsaInterrupt::Floppy,
186        IsaInterrupt::Lpt1,
187        IsaInterrupt::CmosRtc,
188        IsaInterrupt::Periph1,
189        IsaInterrupt::Periph2,
190        IsaInterrupt::Periph3,
191        IsaInterrupt::Ps2Mouse,
192        IsaInterrupt::Fpu,
193        IsaInterrupt::AtaPrimary,
194        IsaInterrupt::AtaSecondary,
195    ];
196}
197
198#[must_use]
199fn disable_scoped() -> impl Drop + Send + Sync {
200    unsafe {
201        crate::cpu::intrinsics::cli();
202    }
203    mycelium_util::defer(|| unsafe {
204        crate::cpu::intrinsics::sti();
205    })
206}
207
208impl Controller {
209    // const DEFAULT_IOAPIC_BASE_PADDR: u64 = 0xFEC00000;
210
211    pub fn idt() -> MutexGuard<'static, idt::Idt, Spinlock> {
212        IDT.lock()
213    }
214
215    #[tracing::instrument(level = "info", name = "interrupt::Controller::init")]
216    pub fn init<H: Handlers<Registers>>() {
217        tracing::info!("intializing IDT...");
218
219        let mut idt = IDT.lock();
220        idt.register_handlers::<H>().unwrap();
221        unsafe {
222            idt.load_raw();
223        }
224    }
225
226    pub fn mask_isa_irq(&self, irq: IsaInterrupt) {
227        match self.model {
228            InterruptModel::Pic(ref pics) => pics.lock().mask(irq),
229            InterruptModel::Apic { ref io, .. } => io.set_isa_masked(irq, true),
230        }
231    }
232
233    pub fn unmask_isa_irq(&self, irq: IsaInterrupt) {
234        match self.model {
235            InterruptModel::Pic(ref pics) => pics.lock().unmask(irq),
236            InterruptModel::Apic { ref io, .. } => io.set_isa_masked(irq, false),
237        }
238    }
239
240    fn local_apic_handle(&self) -> Result<&apic::local::Handle, apic::local::LocalApicError> {
241        match self.model {
242            InterruptModel::Pic(_) => Err(apic::local::LocalApicError::NoApic),
243            InterruptModel::Apic { ref local, .. } => Ok(local),
244        }
245    }
246
247    pub fn with_local_apic<T>(
248        &self,
249        f: impl FnOnce(&LocalApic) -> T,
250    ) -> Result<T, apic::local::LocalApicError> {
251        self.local_apic_handle()?.with(f)
252    }
253
254    /// This should *not* be called by the boot processor
255    pub fn initialize_local_apic<A>(
256        &self,
257        frame_alloc: &A,
258        pagectrl: &mut impl page::Map<mm::size::Size4Kb, A>,
259    ) -> Result<(), apic::local::LocalApicError>
260    where
261        A: page::Alloc<mm::size::Size4Kb>,
262    {
263        let _deferred = disable_scoped();
264        let hdl = self.local_apic_handle()?;
265        unsafe {
266            hdl.initialize(frame_alloc, pagectrl, Idt::LOCAL_APIC_SPURIOUS as u8);
267        }
268        Ok(())
269    }
270    /// # Safety
271    ///
272    /// Calling this when there isn't actually an ISA interrupt pending can do
273    /// arbitrary bad things (which I think is basically just faulting the CPU).
274    pub unsafe fn end_isa_irq(&self, irq: IsaInterrupt) {
275        match self.model {
276            InterruptModel::Pic(ref pics) => pics.lock().end_interrupt(irq),
277            InterruptModel::Apic { ref local, .. } => local.with(|apic| unsafe { apic.end_interrupt() })
278                .expect("interrupts should not be handled on this core until the local APIC is initialized")
279        }
280    }
281
282    pub fn enable_hardware_interrupts(
283        acpi: Option<&acpi::InterruptModel>,
284        frame_alloc: &impl page::Alloc<mm::size::Size4Kb>,
285    ) -> &'static Self {
286        let mut pics = pic::CascadedPic::new();
287        // regardless of whether APIC or PIC interrupt handling will be used,
288        // the PIC interrupt vectors must be remapped so that they do not
289        // conflict with CPU exceptions.
290        unsafe {
291            tracing::debug!(
292                big = Idt::PIC_BIG_START,
293                little = Idt::PIC_LITTLE_START,
294                "remapping PIC interrupt vectors"
295            );
296            pics.set_irq_address(Idt::PIC_BIG_START as u8, Idt::PIC_LITTLE_START as u8);
297        }
298
299        let controller = match acpi {
300            Some(acpi::InterruptModel::Apic(apic_info)) => {
301                tracing::info!("detected APIC interrupt model");
302
303                let mut pagectrl = mm::PageCtrl::current();
304
305                // disable the 8259 PICs so that we can use APIC interrupts instead
306                unsafe {
307                    pics.disable();
308                }
309                tracing::info!("disabled 8259 PICs");
310
311                // configure the I/O APIC(s)
312                let io = IoApicSet::new(apic_info, frame_alloc, &mut pagectrl, Idt::ISA_BASE as u8);
313
314                // configure and initialize the local APIC on the boot processor
315                let local = apic::local::Handle::new();
316                unsafe {
317                    local.initialize(frame_alloc, &mut pagectrl, Idt::LOCAL_APIC_SPURIOUS as u8);
318                }
319
320                let model = InterruptModel::Apic { local, io };
321
322                tracing::trace!(interrupt_model = ?model);
323
324                INTERRUPT_CONTROLLER.init(Self { model })
325            }
326            model => {
327                if model.is_none() {
328                    tracing::warn!("platform does not support ACPI; falling back to 8259 PIC");
329                } else {
330                    tracing::warn!(
331                        "ACPI does not indicate APIC interrupt model; falling back to 8259 PIC"
332                    )
333                }
334                tracing::info!("configuring 8259 PIC interrupts...");
335
336                unsafe {
337                    // functionally a no-op, since interrupts from PC/AT PIC are enabled at boot, just being
338                    // clear for you, the reader, that at this point they are definitely intentionally enabled.
339                    pics.enable();
340                }
341                INTERRUPT_CONTROLLER.init(Self {
342                    model: InterruptModel::Pic(Mutex::new_with_raw_mutex(pics, Spinlock::new())),
343                })
344            }
345        };
346
347        unsafe {
348            crate::cpu::intrinsics::sti();
349        }
350
351        // There's a weird behavior in QEMU where, apparently, when we unmask
352        // the PIT interrupt, it might fire spuriously *as soon as its
353        // unmasked*, even if we haven't actually done an `sti`. I don't get
354        // this and it seems wrong, but it seems to happen occasionally with the
355        // default QEMU acceleration, and pretty consistently with `-machine
356        // accel=kvm`, so *maybe* it can also happen on a real computer?
357        //
358        // Anyway, because of this, we can't unmask the PIT interrupt until
359        // after we've actually initialized the interrupt controller static.
360        // Otherwise, if we unmask it before initializing the static (like we
361        // used to), the interrupt gets raised immediately, and when the ISR
362        // tries to actually send an EOI to ack it, it dereferences
363        // uninitialized memory and the kernel just hangs. So, we wait to do it
364        // until here.
365        //
366        // The fact that this behavior exists at all makes me really, profoundly
367        // uncomfortable: shouldn't `cli` like, actually do what it's supposed
368        // to? But, we can just choose to unmask the PIT later and it's fine, I
369        // guess...
370        controller.unmask_isa_irq(IsaInterrupt::PitTimer);
371        controller.unmask_isa_irq(IsaInterrupt::Ps2Keyboard);
372        controller
373    }
374
375    /// Starts a periodic timer which fires the `timer_tick` interrupt of the
376    /// provided [`Handlers`] every time `interval` elapses.
377    pub fn start_periodic_timer(&self, interval: Duration) -> Result<(), PeriodicTimerError> {
378        match self.model {
379            InterruptModel::Pic(_) => crate::time::PIT
380                .lock()
381                .start_periodic_timer(interval)
382                .map_err(Into::into),
383            InterruptModel::Apic { ref local, .. } => local.with(|apic| {
384                // divide by 16 is chosen kinda arbitrarily lol
385                apic.calibrate_timer(apic::local::register::TimerDivisor::By16);
386                apic.start_periodic_timer(interval, Idt::LOCAL_APIC_TIMER as u8)?;
387                Ok(())
388            })?,
389        }
390    }
391}
392
393impl<T> hal_core::interrupt::Context for Context<'_, T> {
394    type Registers = Registers;
395
396    fn registers(&self) -> &Registers {
397        self.registers
398    }
399
400    /// # Safety
401    ///
402    /// Mutating the value of saved interrupt registers can cause
403    /// undefined behavior.
404    unsafe fn registers_mut(&mut self) -> &mut Registers {
405        self.registers
406    }
407}
408
409impl ctx::PageFault for Context<'_, PageFaultCode> {
410    fn fault_vaddr(&self) -> crate::VAddr {
411        crate::control_regs::Cr2::read()
412    }
413
414    fn debug_error_code(&self) -> &dyn fmt::Debug {
415        &self.code
416    }
417
418    fn display_error_code(&self) -> &dyn fmt::Display {
419        &self.code
420    }
421}
422
423impl ctx::CodeFault for Context<'_, CodeFault<'_>> {
424    fn is_user_mode(&self) -> bool {
425        false // TODO(eliza)
426    }
427
428    fn instruction_ptr(&self) -> crate::VAddr {
429        self.registers.instruction_ptr
430    }
431
432    fn fault_kind(&self) -> &'static str {
433        self.code.kind
434    }
435
436    fn details(&self) -> Option<&dyn fmt::Display> {
437        self.code.error_code
438    }
439}
440
441impl Context<'_, ErrorCode> {
442    pub fn error_code(&self) -> ErrorCode {
443        self.code
444    }
445}
446
447impl Context<'_, PageFaultCode> {
448    pub fn page_fault_code(&self) -> PageFaultCode {
449        self.code
450    }
451}
452
453impl hal_core::interrupt::Control for Idt {
454    // type Vector = u8;
455    type Registers = Registers;
456
457    #[inline]
458    unsafe fn disable(&mut self) {
459        crate::cpu::intrinsics::cli();
460    }
461
462    #[inline]
463    unsafe fn enable(&mut self) {
464        crate::cpu::intrinsics::sti();
465        tracing::trace!("interrupts enabled");
466    }
467
468    fn is_enabled(&self) -> bool {
469        unimplemented!("eliza do this one!!!")
470    }
471
472    fn register_handlers<H>(&mut self) -> Result<(), hal_core::interrupt::RegistrationError>
473    where
474        H: Handlers<Registers>,
475    {
476        let span = tracing::debug_span!("Idt::register_handlers");
477        let _enter = span.enter();
478
479        // === exceptions ===
480        // these exceptions are mapped to the HAL `Handlers` trait's "code
481        // fault" handler, and indicate that the code that was executing did a
482        // Bad Thing
483        self.register_isr(Self::DIVIDE_BY_ZERO, isr::div_0::<H> as *const ());
484        self.register_isr(Self::OVERFLOW, isr::overflow::<H> as *const ());
485        self.register_isr(Self::BOUND_RANGE_EXCEEDED, isr::br::<H> as *const ());
486        self.register_isr(Self::INVALID_OPCODE, isr::ud::<H> as *const ());
487        self.register_isr(Self::DEVICE_NOT_AVAILABLE, isr::no_fpu::<H> as *const ());
488        self.register_isr(
489            Self::ALIGNMENT_CHECK,
490            isr::alignment_check::<H> as *const (),
491        );
492        self.register_isr(
493            Self::SIMD_FLOATING_POINT,
494            isr::simd_fp_exn::<H> as *const (),
495        );
496        self.register_isr(Self::X87_FPU_EXCEPTION, isr::x87_exn::<H> as *const ());
497
498        // other exceptions, not mapped to the "code fault" handler
499        self.register_isr(Self::PAGE_FAULT, isr::page_fault::<H> as *const ());
500        self.register_isr(Self::INVALID_TSS, isr::invalid_tss::<H> as *const ());
501        self.register_isr(
502            Self::SEGMENT_NOT_PRESENT,
503            isr::segment_not_present::<H> as *const (),
504        );
505        self.register_isr(
506            Self::STACK_SEGMENT_FAULT,
507            isr::stack_segment::<H> as *const (),
508        );
509        self.register_isr(Self::GENERAL_PROTECTION_FAULT, isr::gpf::<H> as *const ());
510        self.register_isr(Self::DOUBLE_FAULT, isr::double_fault::<H> as *const ());
511
512        // === hardware interrupts ===
513        // ISA standard hardware interrupts mapped on both the PICs and IO APIC
514        // interrupt models.
515        self.register_isa_isr(IsaInterrupt::PitTimer, isr::pit_timer::<H> as *const ());
516        self.register_isa_isr(IsaInterrupt::Ps2Keyboard, isr::keyboard::<H> as *const ());
517
518        // local APIC specific hardware interrupts
519        self.register_isr(Self::LOCAL_APIC_SPURIOUS, isr::spurious as *const ());
520        self.register_isr(Self::LOCAL_APIC_TIMER, isr::apic_timer::<H> as *const ());
521
522        // vector 69 (nice) is reserved by the HAL for testing the IDT.
523        self.register_isr(69, isr::test::<H> as *const ());
524
525        Ok(())
526    }
527}
528
529/// Forcefully unlock the VGA port and COM1 serial port (used by tracing), so
530/// that an ISR can log stuff without deadlocking.
531///
532/// # Safety
533///
534/// This forcefully unlocks a mutex, which is probably bad to do. Only do this
535/// in ISRs that definitely represent real actual faults, and not just because
536/// "you wanted to log something".
537unsafe fn force_unlock_tracing() {
538    crate::vga::writer().force_unlock();
539    if let Some(com1) = crate::serial::com1() {
540        com1.force_unlock();
541    }
542}
543
544impl fmt::Debug for Registers {
545    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
546        let Self {
547            instruction_ptr,
548            code_segment,
549            stack_ptr,
550            stack_segment,
551            _pad: _,
552            cpu_flags,
553            _pad2: _,
554        } = self;
555        f.debug_struct("Registers")
556            .field("instruction_ptr", instruction_ptr)
557            .field("code_segment", code_segment)
558            .field("cpu_flags", &format_args!("{cpu_flags:#b}"))
559            .field("stack_ptr", stack_ptr)
560            .field("stack_segment", stack_segment)
561            .finish()
562    }
563}
564
565impl fmt::Display for Registers {
566    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
567        writeln!(f, "  rip:   {:?}", self.instruction_ptr)?;
568        writeln!(f, "  cs:    {:?}", self.code_segment)?;
569        writeln!(f, "  flags: {:#b}", self.cpu_flags)?;
570        writeln!(f, "  rsp:   {:?}", self.stack_ptr)?;
571        writeln!(f, "  ss:    {:?}", self.stack_segment)?;
572        Ok(())
573    }
574}
575
576pub fn fire_test_interrupt() {
577    unsafe { asm!("int {0}", const 69) }
578}
579
580// === impl SelectorErrorCode ===
581
582impl SelectorErrorCode {
583    #[inline]
584    fn named(self, segment_kind: &'static str) -> NamedSelectorErrorCode {
585        NamedSelectorErrorCode {
586            segment_kind,
587            code: self,
588        }
589    }
590
591    fn display(&self) -> impl fmt::Display {
592        struct PrettyErrorCode(SelectorErrorCode);
593
594        impl fmt::Display for PrettyErrorCode {
595            fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
596                let table = self.0.get(SelectorErrorCode::TABLE);
597                let index = self.0.get(SelectorErrorCode::INDEX);
598                write!(f, "{table} index {index}")?;
599                if self.0.get(SelectorErrorCode::EXTERNAL) {
600                    f.write_str(" (from an external source)")?;
601                }
602                write!(f, " (error code {:#b})", self.0.bits())?;
603
604                Ok(())
605            }
606        }
607
608        PrettyErrorCode(*self)
609    }
610}
611
612struct NamedSelectorErrorCode {
613    segment_kind: &'static str,
614    code: SelectorErrorCode,
615}
616
617impl fmt::Display for NamedSelectorErrorCode {
618    #[inline]
619    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
620        write!(f, "{} at {}", self.segment_kind, self.code.display())
621    }
622}
623
624mod isr {
625    use super::*;
626
627    macro_rules! gen_code_faults {
628        ($(fn $name:ident($($rest:tt)+),)+) => {
629            $(
630                gen_code_faults! {@ $name($($rest)+); }
631            )+
632        };
633        (@ $name:ident($kind:literal);) => {
634            pub(super) extern "x86-interrupt" fn $name<H: Handlers<Registers>>(mut registers: Registers) {
635                let code = CodeFault {
636                    error_code: None,
637                    kind: $kind,
638                };
639                H::code_fault(Context { registers: &mut registers, code });
640            }
641        };
642        (@ $name:ident($kind:literal, code);) => {
643            pub(super) extern "x86-interrupt" fn $name<H: Handlers<Registers>>(
644                mut registers: Registers,
645                code: u64,
646            ) {
647                let code = CodeFault {
648                    error_code: Some(&code),
649                    kind: $kind,
650                };
651                H::code_fault(Context {  registers: &mut registers, code });
652            }
653        };
654    }
655
656    gen_code_faults! {
657        fn div_0("Divide-By-Zero (0x0)"),
658        fn overflow("Overflow (0x4)"),
659        fn br("Bound Range Exceeded (0x5)"),
660        fn ud("Invalid Opcode (0x6)"),
661        fn no_fpu("Device (FPU) Not Available (0x7)"),
662        fn alignment_check("Alignment Check (0x11)", code),
663        fn simd_fp_exn("SIMD Floating-Point Exception (0x13)"),
664        fn x87_exn("x87 Floating-Point Exception (0x10)"),
665    }
666
667    pub(super) extern "x86-interrupt" fn page_fault<H: Handlers<Registers>>(
668        mut registers: Registers,
669        code: PageFaultCode,
670    ) {
671        H::page_fault(Context {
672            registers: &mut registers,
673            code,
674        });
675    }
676
677    pub(super) extern "x86-interrupt" fn double_fault<H: Handlers<Registers>>(
678        mut registers: Registers,
679        code: u64,
680    ) {
681        H::double_fault(Context {
682            registers: &mut registers,
683            code,
684        });
685    }
686
687    pub(super) extern "x86-interrupt" fn pit_timer<H: Handlers<Registers>>(_regs: Registers) {
688        if crate::time::Pit::handle_interrupt() {
689            H::timer_tick()
690        }
691        unsafe {
692            INTERRUPT_CONTROLLER
693                .get_unchecked()
694                .end_isa_irq(IsaInterrupt::PitTimer);
695        }
696    }
697
698    pub(super) extern "x86-interrupt" fn apic_timer<H: Handlers<Registers>>(_regs: Registers) {
699        H::timer_tick();
700        unsafe {
701            match INTERRUPT_CONTROLLER.get_unchecked().model {
702                InterruptModel::Pic(_) => unreachable!(),
703                InterruptModel::Apic { ref local, .. } => {
704                    match local.with(|apic| apic.end_interrupt()) {
705                        Ok(_) => {}
706                        Err(e) => unreachable!(
707                            "the local APIC timer will not have fired if the \
708                             local APIC is uninitialized on this core! {e:?}",
709                        ),
710                    }
711                }
712            }
713        }
714    }
715
716    pub(super) extern "x86-interrupt" fn keyboard<H: Handlers<Registers>>(_regs: Registers) {
717        // 0x60 is a magic PC/AT number.
718        static PORT: cpu::Port = cpu::Port::at(0x60);
719        // load-bearing read - if we don't read from the keyboard controller it won't
720        // send another interrupt on later keystrokes.
721        let scancode = unsafe { PORT.readb() };
722        H::ps2_keyboard(scancode);
723        unsafe {
724            INTERRUPT_CONTROLLER
725                .get_unchecked()
726                .end_isa_irq(IsaInterrupt::Ps2Keyboard);
727        }
728    }
729
730    pub(super) extern "x86-interrupt" fn test<H: Handlers<Registers>>(mut registers: Registers) {
731        H::test_interrupt(Context {
732            registers: &mut registers,
733            code: (),
734        });
735    }
736
737    pub(super) extern "x86-interrupt" fn invalid_tss<H: Handlers<Registers>>(
738        mut registers: Registers,
739        code: u64,
740    ) {
741        unsafe {
742            // Safety: An invalid TSS exception is always an oops. Since we're
743            // not coming back from this, it's okay to forcefully unlock the
744            // tracing outputs.
745            force_unlock_tracing();
746        }
747        let selector = SelectorErrorCode(code as u16);
748        tracing::error!(?selector, "invalid task-state segment!");
749
750        let msg = selector.named("task-state segment (TSS)");
751        let code = CodeFault {
752            error_code: Some(&msg),
753            kind: "Invalid TSS (0xA)",
754        };
755        H::code_fault(Context {
756            registers: &mut registers,
757            code,
758        });
759    }
760
761    pub(super) extern "x86-interrupt" fn segment_not_present<H: Handlers<Registers>>(
762        mut registers: Registers,
763        code: u64,
764    ) {
765        unsafe {
766            // Safety: An segment not present exception is always an oops.
767            // Since we're not coming back from this, it's okay to
768            // forcefully unlock the tracing outputs.
769            force_unlock_tracing();
770        }
771        let selector = SelectorErrorCode(code as u16);
772        tracing::error!(?selector, "a segment was not present!");
773
774        let msg = selector.named("stack segment");
775        let code = CodeFault {
776            error_code: Some(&msg),
777            kind: "Segment Not Present (0xB)",
778        };
779        H::code_fault(Context {
780            registers: &mut registers,
781            code,
782        });
783    }
784
785    pub(super) extern "x86-interrupt" fn stack_segment<H: Handlers<Registers>>(
786        mut registers: Registers,
787        code: u64,
788    ) {
789        unsafe {
790            // Safety: An stack-segment fault exeption is always an oops.
791            // Since we're not coming back from this, it's okay to
792            // forcefully unlock the tracing outputs.
793            force_unlock_tracing();
794        }
795        let selector = SelectorErrorCode(code as u16);
796        tracing::error!(?selector, "a stack-segment fault is happening");
797
798        let msg = selector.named("stack segment");
799        let code = CodeFault {
800            error_code: Some(&msg),
801            kind: "Stack-Segment Fault (0xC)",
802        };
803        H::code_fault(Context {
804            registers: &mut registers,
805            code,
806        });
807    }
808
809    pub(super) extern "x86-interrupt" fn gpf<H: Handlers<Registers>>(
810        mut registers: Registers,
811        code: u64,
812    ) {
813        unsafe {
814            // Safety: A general protection fault is (currently) always an
815            // oops. Since we're not coming back from this, it's okay to
816            // forcefully unlock the tracing outputs.
817            //
818            // TODO(eliza): in the future, if we allow the kernel to
819            // recover from general protection faults in user mode programs,
820            // rather than treating them as invariably fatal, we should
821            // probably not always do this. Instead, we should just handle
822            // the user-mode GPF non-fatally, and only unlock the tracing
823            // stuff if we know we're going to do a kernel oops...
824            force_unlock_tracing();
825        }
826
827        let segment = if code > 0 {
828            Some(SelectorErrorCode(code as u16))
829        } else {
830            None
831        };
832
833        tracing::error!(?segment, "lmao, a general protection fault is happening");
834        let error_code = segment.map(|seg| seg.named("selector"));
835        let code = CodeFault {
836            error_code: error_code.as_ref().map(|code| code as &dyn fmt::Display),
837            kind: "General Protection Fault (0xD)",
838        };
839        H::code_fault(Context {
840            registers: &mut registers,
841            code,
842        });
843    }
844
845    pub(super) extern "x86-interrupt" fn spurious() {
846        // TODO(eliza): do we need to actually do something here?
847    }
848}
849
850#[cfg(test)]
851mod tests {
852    use super::*;
853    use core::mem::size_of;
854
855    #[test]
856    fn registers_is_correct_size() {
857        assert_eq!(size_of::<Registers>(), 40);
858    }
859}