Skip to main content

x86_64/instructions/
interrupts.rs

1//! Enabling and disabling interrupts
2
3use core::arch::asm;
4
5/// Returns whether interrupts are enabled.
6#[inline]
7pub fn are_enabled() -> bool {
8    use crate::registers::rflags::{self, RFlags};
9
10    rflags::read().contains(RFlags::INTERRUPT_FLAG)
11}
12
13/// Enable interrupts.
14///
15/// This is a wrapper around the `sti` instruction.
16///
17/// This function executes `sti; nop` to ensure that the interrupt shadow
18/// caused by the `sti` instruction doesn't last beyond this function.
19/// Use [`enable_and_hlt`] to execute `hlt` in `sti`'s interrupt shadow.
20#[inline]
21pub fn enable() {
22    // Omit `nomem` to imitate a lock release. Otherwise, the compiler
23    // is free to move reads and writes through this asm block.
24    unsafe {
25        asm!("sti", "nop", options(preserves_flags, nostack));
26    }
27}
28
29/// Disable interrupts.
30///
31/// This is a wrapper around the `cli` instruction.
32#[inline]
33pub fn disable() {
34    // Omit `nomem` to imitate a lock acquire. Otherwise, the compiler
35    // is free to move reads and writes through this asm block.
36    unsafe {
37        asm!("cli", options(preserves_flags, nostack));
38    }
39}
40
41/// Run a closure with disabled interrupts.
42///
43/// Run the given closure, disabling interrupts before running it (if they aren't already disabled).
44/// Afterwards, interrupts are enabling again if they were enabled before.
45///
46/// If you have other `enable` and `disable` calls _within_ the closure, things may not work as expected.
47///
48/// # Examples
49///
50/// ```ignore
51/// // interrupts are enabled
52/// without_interrupts(|| {
53///     // interrupts are disabled
54///     without_interrupts(|| {
55///         // interrupts are disabled
56///     });
57///     // interrupts are still disabled
58/// });
59/// // interrupts are enabled again
60/// ```
61#[inline]
62pub fn without_interrupts<F, R>(f: F) -> R
63where
64    F: FnOnce() -> R,
65{
66    // true if the interrupt flag is set (i.e. interrupts are enabled)
67    let saved_intpt_flag = are_enabled();
68
69    // if interrupts are enabled, disable them for now
70    if saved_intpt_flag {
71        disable();
72    }
73
74    // do `f` while interrupts are disabled
75    let ret = f();
76
77    // re-enable interrupts if they were previously enabled
78    if saved_intpt_flag {
79        enable();
80    }
81
82    // return the result of `f` to the caller
83    ret
84}
85
86/// Atomically enable interrupts and put the CPU to sleep
87///
88/// Executes the `sti; hlt` instruction sequence. Since the `sti` instruction
89/// keeps interrupts disabled until after the immediately following
90/// instruction (called "interrupt shadow"), no interrupt can occur between the
91/// two instructions. (One exception to this are non-maskable interrupts; this
92/// is explained below.)
93///
94/// This function is useful to put the CPU to sleep without missing interrupts
95/// that occur immediately before the `hlt` instruction:
96///
97/// ```ignore
98/// // there is a race between the check and the `hlt` instruction here:
99///
100/// if nothing_to_do() {
101///     // <- race when the interrupt occurs here
102///     x86_64::instructions::hlt(); // wait for the next interrupt
103/// }
104///
105/// // avoid this race by using `enable_and_hlt`:
106///
107/// x86_64::instructions::interrupts::disable();
108/// if nothing_to_do() {
109///     // <- no interrupts can occur here (interrupts are disabled)
110///     x86_64::instructions::interrupts::enable_and_hlt();
111/// }
112///
113/// ```
114///
115/// ## Non-maskable Interrupts
116///
117/// On some processors, the interrupt shadow of `sti` does not apply to
118/// non-maskable interrupts (NMIs). This means that an NMI can occur between
119/// the `sti` and `hlt` instruction, with the result that the CPU is put to
120/// sleep even though a new interrupt occurred.
121///
122/// To work around this, it is recommended to check in the NMI handler if
123/// the interrupt occurred between `sti` and `hlt` instructions. If this is the
124/// case, the handler should increase the instruction pointer stored in the
125/// interrupt stack frame so that the `hlt` instruction is skipped.
126///
127/// See <http://lkml.iu.edu/hypermail/linux/kernel/1009.2/01406.html> for more
128/// information.
129#[inline]
130pub fn enable_and_hlt() {
131    unsafe {
132        asm!("sti; hlt", options(nomem, nostack));
133    }
134}
135
136/// Cause a breakpoint exception by invoking the `int3` instruction.
137#[inline]
138pub fn int3() {
139    unsafe {
140        asm!("int3", options(nomem, nostack));
141    }
142}
143
144/// Generate a software interrupt by invoking the `int` instruction.
145///
146/// ## Safety
147///
148/// Invoking an arbitrary interrupt is unsafe. It can cause your system to
149/// crash if you invoke a double-fault (#8) or machine-check (#18) exception.
150/// It can also cause memory/register corruption depending on the interrupt
151/// implementation (if it expects values/pointers to be passed in registers).
152#[cfg(feature = "asm_const")]
153pub unsafe fn software_interrupt<const NUM: u8>() {
154    unsafe {
155        asm!("int {num}", num = const NUM, options(nomem, nostack));
156    }
157}