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.unmask_isa_irq(IsaInterrupt::Com1);
373        controller
374    }
375
376    /// Starts a periodic timer which fires the `timer_tick` interrupt of the
377    /// provided [`Handlers`] every time `interval` elapses.
378    pub fn start_periodic_timer(&self, interval: Duration) -> Result<(), PeriodicTimerError> {
379        match self.model {
380            InterruptModel::Pic(_) => crate::time::PIT
381                .lock()
382                .start_periodic_timer(interval)
383                .map_err(Into::into),
384            InterruptModel::Apic { ref local, .. } => local.with(|apic| {
385                // divide by 16 is chosen kinda arbitrarily lol
386                apic.calibrate_timer(apic::local::register::TimerDivisor::By16);
387                apic.start_periodic_timer(interval, Idt::LOCAL_APIC_TIMER as u8)?;
388                Ok(())
389            })?,
390        }
391    }
392}
393
394impl<T> hal_core::interrupt::Context for Context<'_, T> {
395    type Registers = Registers;
396
397    fn registers(&self) -> &Registers {
398        self.registers
399    }
400
401    /// # Safety
402    ///
403    /// Mutating the value of saved interrupt registers can cause
404    /// undefined behavior.
405    unsafe fn registers_mut(&mut self) -> &mut Registers {
406        self.registers
407    }
408}
409
410impl ctx::PageFault for Context<'_, PageFaultCode> {
411    fn fault_vaddr(&self) -> crate::VAddr {
412        crate::control_regs::Cr2::read()
413    }
414
415    fn debug_error_code(&self) -> &dyn fmt::Debug {
416        &self.code
417    }
418
419    fn display_error_code(&self) -> &dyn fmt::Display {
420        &self.code
421    }
422}
423
424impl ctx::CodeFault for Context<'_, CodeFault<'_>> {
425    fn is_user_mode(&self) -> bool {
426        false // TODO(eliza)
427    }
428
429    fn instruction_ptr(&self) -> crate::VAddr {
430        self.registers.instruction_ptr
431    }
432
433    fn fault_kind(&self) -> &'static str {
434        self.code.kind
435    }
436
437    fn details(&self) -> Option<&dyn fmt::Display> {
438        self.code.error_code
439    }
440}
441
442impl Context<'_, ErrorCode> {
443    pub fn error_code(&self) -> ErrorCode {
444        self.code
445    }
446}
447
448impl Context<'_, PageFaultCode> {
449    pub fn page_fault_code(&self) -> PageFaultCode {
450        self.code
451    }
452}
453
454impl hal_core::interrupt::Control for Idt {
455    // type Vector = u8;
456    type Registers = Registers;
457
458    #[inline]
459    unsafe fn disable(&mut self) {
460        crate::cpu::intrinsics::cli();
461    }
462
463    #[inline]
464    unsafe fn enable(&mut self) {
465        crate::cpu::intrinsics::sti();
466        tracing::trace!("interrupts enabled");
467    }
468
469    fn is_enabled(&self) -> bool {
470        unimplemented!("eliza do this one!!!")
471    }
472
473    fn register_handlers<H>(&mut self) -> Result<(), hal_core::interrupt::RegistrationError>
474    where
475        H: Handlers<Registers>,
476    {
477        let span = tracing::debug_span!("Idt::register_handlers");
478        let _enter = span.enter();
479
480        // === exceptions ===
481        // these exceptions are mapped to the HAL `Handlers` trait's "code
482        // fault" handler, and indicate that the code that was executing did a
483        // Bad Thing
484        self.register_isr(Self::DIVIDE_BY_ZERO, isr::div_0::<H> as *const ());
485        self.register_isr(Self::OVERFLOW, isr::overflow::<H> as *const ());
486        self.register_isr(Self::BOUND_RANGE_EXCEEDED, isr::br::<H> as *const ());
487        self.register_isr(Self::INVALID_OPCODE, isr::ud::<H> as *const ());
488        self.register_isr(Self::DEVICE_NOT_AVAILABLE, isr::no_fpu::<H> as *const ());
489        self.register_isr(
490            Self::ALIGNMENT_CHECK,
491            isr::alignment_check::<H> as *const (),
492        );
493        self.register_isr(
494            Self::SIMD_FLOATING_POINT,
495            isr::simd_fp_exn::<H> as *const (),
496        );
497        self.register_isr(Self::X87_FPU_EXCEPTION, isr::x87_exn::<H> as *const ());
498
499        // other exceptions, not mapped to the "code fault" handler
500        self.register_isr(Self::PAGE_FAULT, isr::page_fault::<H> as *const ());
501        self.register_isr(Self::INVALID_TSS, isr::invalid_tss::<H> as *const ());
502        self.register_isr(
503            Self::SEGMENT_NOT_PRESENT,
504            isr::segment_not_present::<H> as *const (),
505        );
506        self.register_isr(
507            Self::STACK_SEGMENT_FAULT,
508            isr::stack_segment::<H> as *const (),
509        );
510        self.register_isr(Self::GENERAL_PROTECTION_FAULT, isr::gpf::<H> as *const ());
511        self.register_isr(Self::DOUBLE_FAULT, isr::double_fault::<H> as *const ());
512
513        // === hardware interrupts ===
514        // ISA standard hardware interrupts mapped on both the PICs and IO APIC
515        // interrupt models.
516        self.register_isa_isr(IsaInterrupt::PitTimer, isr::pit_timer::<H> as *const ());
517        self.register_isa_isr(IsaInterrupt::Ps2Keyboard, isr::keyboard::<H> as *const ());
518        self.register_isa_isr(IsaInterrupt::Com1, isr::com1::<H> as *const ());
519
520        // local APIC specific hardware interrupts
521        self.register_isr(Self::LOCAL_APIC_SPURIOUS, isr::spurious as *const ());
522        self.register_isr(Self::LOCAL_APIC_TIMER, isr::apic_timer::<H> as *const ());
523
524        // vector 69 (nice) is reserved by the HAL for testing the IDT.
525        self.register_isr(69, isr::test::<H> as *const ());
526
527        Ok(())
528    }
529}
530
531/// Forcefully unlock the VGA port and COM1 serial port (used by tracing), so
532/// that an ISR can log stuff without deadlocking.
533///
534/// # Safety
535///
536/// This forcefully unlocks a mutex, which is probably bad to do. Only do this
537/// in ISRs that definitely represent real actual faults, and not just because
538/// "you wanted to log something".
539unsafe fn force_unlock_tracing() {
540    crate::vga::writer().force_unlock();
541    if let Some(com1) = crate::serial::com1() {
542        com1.force_unlock();
543    }
544}
545
546impl fmt::Debug for Registers {
547    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
548        let Self {
549            instruction_ptr,
550            code_segment,
551            stack_ptr,
552            stack_segment,
553            _pad: _,
554            cpu_flags,
555            _pad2: _,
556        } = self;
557        f.debug_struct("Registers")
558            .field("instruction_ptr", instruction_ptr)
559            .field("code_segment", code_segment)
560            .field("cpu_flags", &format_args!("{cpu_flags:#b}"))
561            .field("stack_ptr", stack_ptr)
562            .field("stack_segment", stack_segment)
563            .finish()
564    }
565}
566
567impl fmt::Display for Registers {
568    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
569        writeln!(f, "  rip:   {:?}", self.instruction_ptr)?;
570        writeln!(f, "  cs:    {:?}", self.code_segment)?;
571        writeln!(f, "  flags: {:#b}", self.cpu_flags)?;
572        writeln!(f, "  rsp:   {:?}", self.stack_ptr)?;
573        writeln!(f, "  ss:    {:?}", self.stack_segment)?;
574        Ok(())
575    }
576}
577
578pub fn fire_test_interrupt() {
579    unsafe { asm!("int {0}", const 69) }
580}
581
582// === impl SelectorErrorCode ===
583
584impl SelectorErrorCode {
585    #[inline]
586    fn named(self, segment_kind: &'static str) -> NamedSelectorErrorCode {
587        NamedSelectorErrorCode {
588            segment_kind,
589            code: self,
590        }
591    }
592
593    fn display(&self) -> impl fmt::Display {
594        struct PrettyErrorCode(SelectorErrorCode);
595
596        impl fmt::Display for PrettyErrorCode {
597            fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
598                let table = self.0.get(SelectorErrorCode::TABLE);
599                let index = self.0.get(SelectorErrorCode::INDEX);
600                write!(f, "{table} index {index}")?;
601                if self.0.get(SelectorErrorCode::EXTERNAL) {
602                    f.write_str(" (from an external source)")?;
603                }
604                write!(f, " (error code {:#b})", self.0.bits())?;
605
606                Ok(())
607            }
608        }
609
610        PrettyErrorCode(*self)
611    }
612}
613
614struct NamedSelectorErrorCode {
615    segment_kind: &'static str,
616    code: SelectorErrorCode,
617}
618
619impl fmt::Display for NamedSelectorErrorCode {
620    #[inline]
621    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
622        write!(f, "{} at {}", self.segment_kind, self.code.display())
623    }
624}
625
626mod isr {
627    use super::*;
628
629    macro_rules! gen_code_faults {
630        ($(fn $name:ident($($rest:tt)+),)+) => {
631            $(
632                gen_code_faults! {@ $name($($rest)+); }
633            )+
634        };
635        (@ $name:ident($kind:literal);) => {
636            pub(super) extern "x86-interrupt" fn $name<H: Handlers<Registers>>(mut registers: Registers) {
637                let code = CodeFault {
638                    error_code: None,
639                    kind: $kind,
640                };
641                H::code_fault(Context { registers: &mut registers, code });
642            }
643        };
644        (@ $name:ident($kind:literal, code);) => {
645            pub(super) extern "x86-interrupt" fn $name<H: Handlers<Registers>>(
646                mut registers: Registers,
647                code: u64,
648            ) {
649                let code = CodeFault {
650                    error_code: Some(&code),
651                    kind: $kind,
652                };
653                H::code_fault(Context {  registers: &mut registers, code });
654            }
655        };
656    }
657
658    gen_code_faults! {
659        fn div_0("Divide-By-Zero (0x0)"),
660        fn overflow("Overflow (0x4)"),
661        fn br("Bound Range Exceeded (0x5)"),
662        fn ud("Invalid Opcode (0x6)"),
663        fn no_fpu("Device (FPU) Not Available (0x7)"),
664        fn alignment_check("Alignment Check (0x11)", code),
665        fn simd_fp_exn("SIMD Floating-Point Exception (0x13)"),
666        fn x87_exn("x87 Floating-Point Exception (0x10)"),
667    }
668
669    pub(super) extern "x86-interrupt" fn page_fault<H: Handlers<Registers>>(
670        mut registers: Registers,
671        code: PageFaultCode,
672    ) {
673        H::page_fault(Context {
674            registers: &mut registers,
675            code,
676        });
677    }
678
679    pub(super) extern "x86-interrupt" fn double_fault<H: Handlers<Registers>>(
680        mut registers: Registers,
681        code: u64,
682    ) {
683        H::double_fault(Context {
684            registers: &mut registers,
685            code,
686        });
687    }
688
689    pub(super) extern "x86-interrupt" fn pit_timer<H: Handlers<Registers>>(_regs: Registers) {
690        if crate::time::Pit::handle_interrupt() {
691            H::timer_tick()
692        }
693        unsafe {
694            INTERRUPT_CONTROLLER
695                .get_unchecked()
696                .end_isa_irq(IsaInterrupt::PitTimer);
697        }
698    }
699
700    pub(super) extern "x86-interrupt" fn apic_timer<H: Handlers<Registers>>(_regs: Registers) {
701        H::timer_tick();
702        unsafe {
703            match INTERRUPT_CONTROLLER.get_unchecked().model {
704                InterruptModel::Pic(_) => unreachable!(),
705                InterruptModel::Apic { ref local, .. } => {
706                    match local.with(|apic| apic.end_interrupt()) {
707                        Ok(_) => {}
708                        Err(e) => unreachable!(
709                            "the local APIC timer will not have fired if the \
710                             local APIC is uninitialized on this core! {e:?}",
711                        ),
712                    }
713                }
714            }
715        }
716    }
717
718    pub(super) extern "x86-interrupt" fn keyboard<H: Handlers<Registers>>(_regs: Registers) {
719        // 0x60 is a magic PC/AT number.
720        static PORT: cpu::Port = cpu::Port::at(0x60);
721        // load-bearing read - if we don't read from the keyboard controller it won't
722        // send another interrupt on later keystrokes.
723        let scancode = unsafe { PORT.readb() };
724        H::ps2_keyboard(scancode);
725        unsafe {
726            INTERRUPT_CONTROLLER
727                .get_unchecked()
728                .end_isa_irq(IsaInterrupt::Ps2Keyboard);
729        }
730    }
731
732    pub(super) extern "x86-interrupt" fn com1<H: Handlers<Registers>>(_regs: Registers) {
733        use crate::serial::Pc16550dInterrupt;
734        let port = crate::serial::com1().expect("can port??");
735
736        // This is the only point we'll read-lock the serial port. See the module comments on
737        // [`crate::serial`] for the locking procedure around the port.
738        let port = port.read_lock();
739
740        let mut port = port.set_non_blocking();
741        let interrupt = port.check_interrupt_type().expect("interrupt is valid");
742        match interrupt {
743            None => {
744                // We get a spurious irq after reading RHR? Why?
745                // But, nothing to do.
746            }
747            Some(Pc16550dInterrupt::CharacterTimeout) => {
748                // Data is available for reading, so do that.
749                let mut b = [0u8];
750                use mycelium_util::io::Read;
751                let res = port.read(&mut b);
752
753                // We've done the read, so unlock the port.
754                core::mem::drop(port);
755
756                // If the read was blocking:
757                // * what did we get interrupted for?
758                // * `b` is bogus
759                //
760                // so dying here is the order of the day.
761                res.expect("read was not blocking");
762
763                H::serial_input(0, b[0]);
764            }
765            Some(other) => {
766                // We're gonna warn about the unhandled 16550 interrupt, but unlock the port for
767                // reading so we don't hold up anyone else waiting to read-lock for management
768                // operations.
769                core::mem::drop(port);
770
771                tracing::warn!("Unhandled 16550 interrupt cause: {other:?}");
772            }
773        }
774
775        unsafe {
776            INTERRUPT_CONTROLLER
777                .get_unchecked()
778                .end_isa_irq(IsaInterrupt::Com1);
779        }
780    }
781
782    pub(super) extern "x86-interrupt" fn test<H: Handlers<Registers>>(mut registers: Registers) {
783        H::test_interrupt(Context {
784            registers: &mut registers,
785            code: (),
786        });
787    }
788
789    pub(super) extern "x86-interrupt" fn invalid_tss<H: Handlers<Registers>>(
790        mut registers: Registers,
791        code: u64,
792    ) {
793        unsafe {
794            // Safety: An invalid TSS exception is always an oops. Since we're
795            // not coming back from this, it's okay to forcefully unlock the
796            // tracing outputs.
797            force_unlock_tracing();
798        }
799        let selector = SelectorErrorCode(code as u16);
800        tracing::error!(?selector, "invalid task-state segment!");
801
802        let msg = selector.named("task-state segment (TSS)");
803        let code = CodeFault {
804            error_code: Some(&msg),
805            kind: "Invalid TSS (0xA)",
806        };
807        H::code_fault(Context {
808            registers: &mut registers,
809            code,
810        });
811    }
812
813    pub(super) extern "x86-interrupt" fn segment_not_present<H: Handlers<Registers>>(
814        mut registers: Registers,
815        code: u64,
816    ) {
817        unsafe {
818            // Safety: An segment not present exception is always an oops.
819            // Since we're not coming back from this, it's okay to
820            // forcefully unlock the tracing outputs.
821            force_unlock_tracing();
822        }
823        let selector = SelectorErrorCode(code as u16);
824        tracing::error!(?selector, "a segment was not present!");
825
826        let msg = selector.named("stack segment");
827        let code = CodeFault {
828            error_code: Some(&msg),
829            kind: "Segment Not Present (0xB)",
830        };
831        H::code_fault(Context {
832            registers: &mut registers,
833            code,
834        });
835    }
836
837    pub(super) extern "x86-interrupt" fn stack_segment<H: Handlers<Registers>>(
838        mut registers: Registers,
839        code: u64,
840    ) {
841        unsafe {
842            // Safety: An stack-segment fault exeption is always an oops.
843            // Since we're not coming back from this, it's okay to
844            // forcefully unlock the tracing outputs.
845            force_unlock_tracing();
846        }
847        let selector = SelectorErrorCode(code as u16);
848        tracing::error!(?selector, "a stack-segment fault is happening");
849
850        let msg = selector.named("stack segment");
851        let code = CodeFault {
852            error_code: Some(&msg),
853            kind: "Stack-Segment Fault (0xC)",
854        };
855        H::code_fault(Context {
856            registers: &mut registers,
857            code,
858        });
859    }
860
861    pub(super) extern "x86-interrupt" fn gpf<H: Handlers<Registers>>(
862        mut registers: Registers,
863        code: u64,
864    ) {
865        unsafe {
866            // Safety: A general protection fault is (currently) always an
867            // oops. Since we're not coming back from this, it's okay to
868            // forcefully unlock the tracing outputs.
869            //
870            // TODO(eliza): in the future, if we allow the kernel to
871            // recover from general protection faults in user mode programs,
872            // rather than treating them as invariably fatal, we should
873            // probably not always do this. Instead, we should just handle
874            // the user-mode GPF non-fatally, and only unlock the tracing
875            // stuff if we know we're going to do a kernel oops...
876            force_unlock_tracing();
877        }
878
879        let segment = if code > 0 {
880            Some(SelectorErrorCode(code as u16))
881        } else {
882            None
883        };
884
885        tracing::error!(?segment, "lmao, a general protection fault is happening");
886        let error_code = segment.map(|seg| seg.named("selector"));
887        let code = CodeFault {
888            error_code: error_code.as_ref().map(|code| code as &dyn fmt::Display),
889            kind: "General Protection Fault (0xD)",
890        };
891        H::code_fault(Context {
892            registers: &mut registers,
893            code,
894        });
895    }
896
897    pub(super) extern "x86-interrupt" fn spurious() {
898        // TODO(eliza): do we need to actually do something here?
899    }
900}
901
902#[cfg(test)]
903mod tests {
904    use super::*;
905    use core::mem::size_of;
906
907    #[test]
908    fn registers_is_correct_size() {
909        assert_eq!(size_of::<Registers>(), 40);
910    }
911}