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.unmask_isa_irq(IsaInterrupt::Com1);
373 controller
374 }
375
376 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 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 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 }
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 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 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 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 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 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 self.register_isr(69, isr::test::<H> as *const ());
526
527 Ok(())
528 }
529}
530
531unsafe 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
582impl 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 static PORT: cpu::Port = cpu::Port::at(0x60);
721 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 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 }
747 Some(Pc16550dInterrupt::CharacterTimeout) => {
748 let mut b = [0u8];
750 use mycelium_util::io::Read;
751 let res = port.read(&mut b);
752
753 core::mem::drop(port);
755
756 res.expect("read was not blocking");
762
763 H::serial_input(0, b[0]);
764 }
765 Some(other) => {
766 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 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 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 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 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 }
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}