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