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}