abyss/interrupt/mod.rs
1//! Interrupt
2#[cfg(doc)]
3use crate::spinlock::SpinLockGuard;
4use crate::{
5 unwind,
6 x86_64::{Rflags, interrupt::InterruptStackFrame, segmentation::Segment},
7};
8use core::{
9 arch::{asm, naked_asm},
10 sync::atomic::{AtomicBool, AtomicIsize, Ordering},
11};
12
13mod entry;
14pub use entry::do_handle_irq;
15
16static PER_CORE_STATE: [InterruptGuardInner; crate::MAX_CPU] =
17 [const { InterruptGuardInner::new() }; crate::MAX_CPU];
18
19struct InterruptGuardInner {
20 initial_state: AtomicBool,
21 cnt: AtomicIsize,
22}
23
24impl InterruptGuardInner {
25 const fn new() -> Self {
26 Self {
27 initial_state: AtomicBool::new(true),
28 cnt: AtomicIsize::new(0),
29 }
30 }
31
32 fn save_nested_interrupt_state(&self, state: InterruptState) {
33 if self.cnt.fetch_add(1, Ordering::SeqCst) == 0 {
34 self.initial_state
35 .store(state == InterruptState::On, Ordering::SeqCst);
36 }
37 }
38
39 fn load_nested_interrupt_state(&self) {
40 let prev = self.cnt.fetch_sub(1, Ordering::SeqCst);
41 assert!(prev > 0, "Mismatched InterruptGuard drop calls: {prev}");
42
43 if prev == 1 && self.initial_state.load(Ordering::SeqCst) {
44 unsafe { InterruptState::enable() };
45 }
46 }
47
48 fn decrement_count(&self) {
49 let prev = self.cnt.fetch_sub(1, Ordering::SeqCst);
50 assert!(prev > 0, "Mismatched InterruptGuard drop calls: {prev}");
51 }
52}
53
54/// Enumeration representing the interrupt state.
55#[derive(PartialEq, Eq, Debug)]
56pub enum InterruptState {
57 /// Interrupts are enabled.
58 On,
59 /// Interrupts are disabled.
60 Off,
61}
62
63impl InterruptState {
64 /// Reads the current interrupt state.
65 ///
66 /// # Returns
67 /// - [`InterruptState::On`] if interrupts are enabled.
68 /// - [`InterruptState::Off`] if interrupts are disabled.
69 pub fn current() -> Self {
70 if Rflags::read().contains(Rflags::IF) {
71 Self::On
72 } else {
73 Self::Off
74 }
75 }
76
77 pub unsafe fn enable() {
78 unsafe {
79 asm!("sti");
80 }
81 }
82
83 pub unsafe fn disable() {
84 unsafe {
85 asm!("cli");
86 }
87 }
88}
89
90/// An RAII-based guard for managing interrupt disabling.
91///
92/// When an `InterruptGuard` is created, interrupts are disabled. When it is
93/// dropped, the interrupt state is restored to what it was before the guard was
94/// created.
95///
96/// **Important:**
97/// - [`InterruptGuard`] instances **must be dropped in reverse order of their
98/// creation** to prevent unintended interrupt state changes.
99/// - Due to Rust's ownership and scoping rules, this invariant is naturally
100/// upheld unless `drop()` is explicitly called prematurely or an
101/// [`InterruptGuard`] is stored in a struct field.
102///
103/// This structure is created using [`InterruptGuard::new`].
104pub struct InterruptGuard {
105 core_id: usize,
106}
107
108impl !Send for InterruptGuard {}
109impl !Sync for InterruptGuard {}
110
111impl InterruptGuard {
112 /// Creates a new `InterruptGuard`, disabling interrupts.
113 ///
114 /// # Behavior
115 /// - Saves the current interrupt state.
116 /// - Disables interrupts (`cli` instruction).
117 ///
118 /// # Returns
119 /// A new instance of `InterruptGuard`, which will restore the original
120 /// interrupt state when dropped.
121 ///
122 /// # Example
123 /// ```rust
124 /// let _guard = InterruptGuard::new(); // Disables interrupts
125 /// // Critical section...
126 /// // Interrupts are restored when `_guard` goes out of scope.
127 /// ```
128 pub fn new() -> Self {
129 let state = InterruptState::current();
130 unsafe { InterruptState::disable() };
131 core::sync::atomic::fence(Ordering::SeqCst);
132
133 let core_id = crate::x86_64::intrinsics::cpuid();
134 let guard = &PER_CORE_STATE[core_id];
135
136 guard.save_nested_interrupt_state(state);
137
138 Self { core_id }
139 }
140
141 pub fn consume(self) {
142 let guard = &PER_CORE_STATE[self.core_id];
143 guard.decrement_count();
144
145 core::mem::forget(self);
146 }
147
148 pub fn is_guarded() -> bool {
149 let core_id = crate::x86_64::intrinsics::cpuid();
150 let guard = &PER_CORE_STATE[core_id];
151 guard.cnt.load(Ordering::SeqCst) > 0
152 }
153}
154
155impl Default for InterruptGuard {
156 fn default() -> Self {
157 Self::new()
158 }
159}
160
161impl Drop for InterruptGuard {
162 fn drop(&mut self) {
163 if self.core_id != crate::x86_64::intrinsics::cpuid() {
164 panic!(
165 "InterruptGuard dropped on different core: {} != {}",
166 self.core_id,
167 crate::x86_64::intrinsics::cpuid()
168 );
169 }
170
171 let guard = &PER_CORE_STATE[crate::x86_64::intrinsics::cpuid()];
172 guard.load_nested_interrupt_state();
173 core::sync::atomic::fence(Ordering::SeqCst);
174 }
175}
176
177/// X86_64's general purpose registers.
178#[repr(C)]
179#[derive(Clone, Copy, Debug, Default)]
180pub struct GeneralPurposeRegisters {
181 /// R15 register.
182 pub r15: usize,
183 /// R14 register.
184 pub r14: usize,
185 /// R13 register.
186 pub r13: usize,
187 /// R12 register.
188 pub r12: usize,
189 /// R11 register.
190 pub r11: usize,
191 /// R10 register.
192 pub r10: usize,
193 /// R9 register.
194 pub r9: usize,
195 /// R8 register.
196 pub r8: usize,
197 /// RSI register.
198 pub rsi: usize,
199 /// RDI register.
200 pub rdi: usize,
201 /// RBP register.
202 pub rbp: usize,
203 /// RDX register.
204 pub rdx: usize,
205 /// RCX register.
206 pub rcx: usize,
207 /// RBX register.
208 pub rbx: usize,
209 /// RAX register.
210 pub rax: usize,
211}
212
213/// x86_64 Trap frame.
214#[repr(C)]
215#[derive(Clone, Copy)]
216pub struct Registers {
217 pub gprs: GeneralPurposeRegisters,
218 error_code: u64,
219 #[doc(hidden)]
220 pub interrupt_stack_frame: InterruptStackFrame,
221}
222
223impl Default for Registers {
224 fn default() -> Self {
225 Self::new()
226 }
227}
228
229impl Registers {
230 /// Creates a new register frame for a user thread.
231 ///
232 /// This function initializes a [`Registers`] structure with default values
233 /// for a new user-space thread.
234 ///
235 /// # Returns
236 /// - A [`Registers`] instance with default values for user-space execution.
237 ///
238 /// # Example
239 /// ```rust
240 /// let mut regs = Registers::new();
241 /// *regs.rip() = 0x400000; // Set entry point
242 /// *regs.rsp() = 0x7FFFFFFFE000; // Set user stack pointer
243 /// ```
244 #[inline]
245 pub fn new() -> Self {
246 Self {
247 gprs: GeneralPurposeRegisters::default(),
248 error_code: 0,
249 interrupt_stack_frame: InterruptStackFrame {
250 rip: 0, /* Entry point of the user program should
251 * be set later. */
252 cs: Segment::UserCode.into_selector(), // User-space code segment.
253 __pad0: 0,
254 __pad1: 0,
255 rflags: Rflags::IF | Rflags::_1, // Enables interrupts.
256 rsp: 0, /* User-space stack pointer should be set before
257 * execution. */
258 ss: Segment::UserData.into_selector(), // User-space stack segment.
259 __pad2: 0,
260 __pad3: 0,
261 },
262 }
263 }
264
265 /// Returns a mutable reference to the instruction pointer (`RIP`).
266 ///
267 /// This function allows modifying the instruction pointer, which determines
268 /// the next instruction the CPU will execute when the thread resumes.
269 ///
270 /// # Returns
271 /// - A mutable reference to the `rip` field in the interrupt stack frame.
272 ///
273 /// # Example
274 /// ```rust
275 /// let mut regs = Registers::new();
276 /// *regs.rip() = 0x400000; // Set the entry point
277 /// ```
278 pub fn rip(&mut self) -> &mut usize {
279 &mut self.interrupt_stack_frame.rip
280 }
281
282 /// Returns a mutable reference to the stack pointer (`RSP`).
283 ///
284 /// This function allows modifying the stack pointer, which should point
285 /// to the top of the stack before execution.
286 ///
287 /// # Returns
288 /// - A mutable reference to the `rsp` field.
289 ///
290 /// # Example
291 /// ```rust
292 /// let mut regs = Registers::new();
293 /// *regs.rsp() = 0x7FFFFFFFE000; // Set the user stack pointer
294 /// ```
295 pub fn rsp(&mut self) -> &mut usize {
296 &mut self.interrupt_stack_frame.rsp
297 }
298
299 /// Launch the frame.
300 ///
301 /// Launches a thread by restoring its saved register state.
302 ///
303 /// This function returns the `never` type (`!`), meaning that once
304 /// executed, there is no way to return to the current execution
305 /// context.
306 ///
307 /// # Safety
308 /// - The kernel must release all temporary resources such as locally
309 /// allocated `Box`, [`SpinLockGuard`], or [`InterruptGuard`] before
310 /// calling this function.
311 ///
312 /// # Behavior
313 /// 1. Restores general-purpose registers from `self.gprs`.
314 /// 2. Enables interrupts.
315 /// 3. Transfers to saved execution state by executing `iretq`.
316 ///
317 /// # Example Usage
318 /// ```rust
319 /// let regs = Registers::new();
320 /// regs.launch(); // This function does not return
321 /// unreachable!() // Execution will never reach here
322 #[unsafe(naked)]
323 pub extern "C" fn launch(&self) -> ! {
324 naked_asm!(
325 "mov rax, [rdi + 0x70]",
326 "mov rbx, [rdi + 0x68]",
327 "mov rcx, [rdi + 0x60]",
328 "mov rdx, [rdi + 0x58]",
329 "mov rbp, [rdi + 0x50]",
330 "mov rsi, [rdi + 0x40]",
331 "mov r8, [rdi + 0x38]",
332 "mov r9, [rdi + 0x30]",
333 "mov r10, [rdi + 0x28]",
334 "mov r11, [rdi + 0x20]",
335 "mov r12, [rdi + 0x18]",
336 "mov r13, [rdi + 0x10]",
337 "mov r14, [rdi + 0x8]",
338 "mov r15, [rdi]",
339 "sti",
340 "lea rsp, [rdi + 0x80]",
341 "mov rdi, [rdi + 0x48]",
342 "iretq"
343 )
344 }
345
346 #[inline]
347 #[doc(hidden)]
348 pub fn to_stack_frame(&self) -> unwind::StackFrame {
349 unwind::StackFrame {
350 rax: self.gprs.rax,
351 rbx: self.gprs.rbx,
352 rcx: self.gprs.rcx,
353 rdx: self.gprs.rdx,
354 rsi: self.gprs.rsi,
355 rdi: self.gprs.rdi,
356 rbp: self.gprs.rbp,
357 rsp: self.interrupt_stack_frame.rsp,
358 r8: self.gprs.r8,
359 r9: self.gprs.r9,
360 r10: self.gprs.r10,
361 r11: self.gprs.r11,
362 r12: self.gprs.r12,
363 r13: self.gprs.r13,
364 r14: self.gprs.r14,
365 r15: self.gprs.r15,
366 rip: self.interrupt_stack_frame.rip,
367 }
368 }
369}
370
371impl core::fmt::Debug for Registers {
372 fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> Result<(), core::fmt::Error> {
373 write!(
374 f,
375 "RAX: {:016x} | RBX: {:016x} | RCX: {:016x} | RDX: {:016x}\n\
376 RSI: {:016x} | RDI: {:016x} | RBP: {:016x} | RSP: {:016x}\n\
377 R8 : {:016x} | R9 : {:016x} | R10: {:016x} | R11: {:016x}\n\
378 R12: {:016x} | R13: {:016x} | R14: {:016x} | R15: {:016x}\n\
379 RIP: {:016x} | Error Code: {:#x} | RFLAGS: {:016x} [{:?}]\n\
380 CS: {:?} | SS: {:?}",
381 self.gprs.rax,
382 self.gprs.rbx,
383 self.gprs.rcx,
384 self.gprs.rdx,
385 self.gprs.rsi,
386 self.gprs.rdi,
387 self.gprs.rbp,
388 self.interrupt_stack_frame.rsp,
389 self.gprs.r8,
390 self.gprs.r9,
391 self.gprs.r10,
392 self.gprs.r11,
393 self.gprs.r12,
394 self.gprs.r13,
395 self.gprs.r14,
396 self.gprs.r15,
397 self.interrupt_stack_frame.rip,
398 self.error_code,
399 self.interrupt_stack_frame.rflags.bits(),
400 self.interrupt_stack_frame.rflags,
401 self.interrupt_stack_frame.cs,
402 self.interrupt_stack_frame.ss,
403 )
404 }
405}
406
407/// NMI Expection for Stopping CPUs on PANIC
408pub static NMI_EXPECTED_PANICKING: AtomicBool = AtomicBool::new(false);