tartan_arch/x86_common/
interrupt.rs

1//! Support for managing interrupts.
2
3use crate::model_specific_register_access;
4use core::arch::asm;
5use tartan_bitfield::bitfield;
6use tartan_c_enum::c_enum;
7
8#[cfg(doc)]
9use super::FlagRegister;
10
11
12c_enum! {
13    /// An interrupt/exception number.
14    pub enum InterruptVector(u8) {
15        /// `#DE`: Division by zero or overflow in division.
16        DivideError = 0,
17        /// `#DB`: Breakpoints or other debugging-related traps/faults.
18        DebugException = 1,
19        /// `NMI`: Severe external interrupt that cannot be ignored.
20        NonMaskable = 2,
21        /// `#BP`: Breakpoint via `INT3` instruction.
22        Breakpoint = 3,
23        /// `#OF`: Overflow condition was detected with `INTO` instruction.
24        Overflow = 4,
25        /// `#BR`: Out-of-bounds index detected with `BOUND` instruction.
26        Bound = 5,
27        /// `#UD`: Unrecognized or reserved instruction opcode.
28        InvalidOpcode = 6,
29        /// `#NM`: Tried to execute FPU instruction with no coprocessor present.
30        DeviceNotAvailable = 7,
31        /// `#DF`: A fault was triggered while handling another interrupt.
32        DoubleFault = 8,
33        /// `#TS`: An error was found in the TSS while task switching.
34        InvalidTaskSegment = 10,
35        /// `#NP`: Tried to use a segment without a descriptor defined. Does not apply to
36        /// the stack segment, which has its [own exception](Self::SegmentNotPresent).
37        SegmentNotPresent = 11,
38        /// `#SS`: The stack overflowed its segment or the segment was invalid.
39        StackFault = 12,
40        /// `#GP`: Memory protection or other miscellaneous error.
41        ProtectionFault = 13,
42        /// `#PF`: Tried to load a page that was not present, or used a page in a way that
43        /// was not allowed by its attributes.
44        PageFault = 14,
45        /// `#MF`: Unmasked floating-point error that was *not* part of a SIMD operation.
46        FloatingPointError = 16,
47        /// `#AC`: Improperly aligned memory access in user mode while alignment checks
48        /// were enabled.
49        ///
50        /// See [`FlagRegister::alignment_check_or_access_control`].
51        AlignmentCheck = 17,
52        /// `#MC`: Internal processor/bus error.
53        MachineCheck = 18,
54        /// `#XM`: Unmasked floating-point error during SIMD operation.
55        SIMDFloatingPointError = 19,
56        /// `#VE`: Improper use of virtualization extensions.
57        ///
58        /// See [`ControlRegister4::virtual_machine_extensions`].
59        VirtualizationException = 20,
60        /// `#CP`: Improper branching detected by control-flow guard.
61        ///
62        /// See [`ControlRegister4::control_flow_enforcement`].
63        ControlProtectionException = 21,
64    }
65}
66
67impl InterruptVector {
68    /// Minimum user-defined interrupt vector.
69    const MIN_USER: InterruptVector = InterruptVector(32);
70
71    /// Indicates that this is interrupt vector is reserved by the system.
72    pub fn reserved(self) -> bool {
73        self < Self::MIN_USER
74    }
75}
76
77
78/// `IDTR`: Contains the memory range of the interrupt descriptor table.
79#[repr(C, packed)]
80#[derive(Debug, Default, Clone, Copy, PartialEq, Eq)]
81#[allow(clippy::module_name_repetitions)]
82pub struct InterruptDescriptorTableRegister {
83    /// The inclusive maximum address offset (i.e., size - 1) of the descriptor table.
84    pub limit: u16,
85    /// Base address of the descriptor table.
86    pub address: usize,
87}
88
89impl InterruptDescriptorTableRegister {
90    /// Retrieve the current value of this register
91    pub fn get() -> Self {
92        let mut value = Self::default();
93        unsafe {
94            asm!(
95                "sidt [{0}]",
96                in(reg) &raw mut value,
97            );
98        }
99        value
100    }
101
102    /// Update the register to the given value.
103    ///
104    /// # Safety
105    /// This register can have fundamental affects on how programs execute.
106    pub unsafe fn set(value: &Self) {
107        asm!(
108            "lidt [{0}]",
109            in(reg) value,
110        );
111    }
112}
113
114
115bitfield! {
116    /// `IA32_APIC_BASE`: A model-specific register that allows relocating an advanced
117    /// programmable interrupt controller (APIC)'s control structure.
118    ///
119    /// See Intel ISA volume 2 §10.4.3–10.4.4.
120    pub struct APICBaseRegister(u64) {
121        /// Can be cleared to disable the APIC. However, it cannot be re-enabled once
122        /// disabled with this mechanism.
123        [11] pub enabled,
124        /// Enable the APIC's
125        [10] pub x2_interface,
126        /// Indicates that this core was designated as the bootstrap processor for
127        /// single-threaded operation on startup.
128        [8] pub bootstrap_processor,
129    }
130}
131
132impl APICBaseRegister {
133    /// The default APIC base address when the processor is reset.
134    pub const DEFAULT_ADDRESS: usize = 0xfee0_0000;
135    const ADDRESS_MASK: u64 = 0xffff_ffff_ffff_f000;
136
137    /// Base address of the APIC's registers.
138    pub fn address(self) -> usize {
139        #![allow(clippy::cast_possible_truncation)]
140        (self.0 & Self::ADDRESS_MASK) as usize
141    }
142
143    /// Update the base address of the APIC's registers.
144    pub fn set_address(&mut self, address: usize) {
145        self.0 &= !Self::ADDRESS_MASK;
146        self.0 |= Self::ADDRESS_MASK & address as u64;
147    }
148}
149
150model_specific_register_access!(APICBaseRegister, 0x1b);