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
42pub 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#[derive(Debug)]
66
67enum InterruptModel {
68 Pic(Mutex<pic::CascadedPic, Spinlock>),
71 Apic {
78 io: apic::IoApicSet,
79 local: apic::local::Handle,
80 },
81}
82
83bits::bitfield! {
84 pub struct PageFaultCode<u32> {
85 pub const PRESENT: bool;
88 pub const WRITE: bool;
91 pub const USER: bool;
94 pub const RESERVED_WRITE: bool;
98 pub const INSTRUCTION_FETCH: bool;
101 pub const PROTECTION_KEY: bool;
105 pub const SHADOW_STACK: bool;
107 const _RESERVED0 = 8;
108 pub const SGX: bool;
111 }
112}
113
114bits::bitfield! {
115 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, pub code_segment: segment::Selector,
131 _pad: [u16; 3],
132 pub cpu_flags: u64, pub stack_ptr: VAddr, 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#[derive(Copy, Clone, Debug)]
145#[repr(u8)]
146pub enum IsaInterrupt {
147 PitTimer = 0,
149 Ps2Keyboard = 1,
151 Com2 = 3,
154 Com1 = 4,
156 Lpt2 = 5,
158 Floppy = 6,
160 Lpt1 = 7,
162 CmosRtc = 8,
164 Periph1 = 9,
166 Periph2 = 10,
167 Periph3 = 11,
168 Ps2Mouse = 12,
170 Fpu = 13,
172 AtaPrimary = 14,
174 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 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 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 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 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 unsafe {
307 pics.disable();
308 }
309 tracing::info!("disabled 8259 PICs");
310
311 let io = IoApicSet::new(apic_info, frame_alloc, &mut pagectrl, Idt::ISA_BASE as u8);
313
314 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 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 controller.unmask_isa_irq(IsaInterrupt::PitTimer);
371 controller.unmask_isa_irq(IsaInterrupt::Ps2Keyboard);
372 controller
373 }
374
375 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 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 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 }
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 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 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 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 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 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 self.register_isr(69, isr::test::<H> as *const ());
524
525 Ok(())
526 }
527}
528
529unsafe 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
580impl 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 static PORT: cpu::Port = cpu::Port::at(0x60);
719 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 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 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 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 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 }
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}