Skip to content

Commit

Permalink
Add an enable_interrupts_and_hlt function that executes sti; hlt
Browse files Browse the repository at this point in the history
  • Loading branch information
phil-opp committed Mar 8, 2020
1 parent 26f332a commit 944cf0e
Show file tree
Hide file tree
Showing 3 changed files with 68 additions and 0 deletions.
7 changes: 7 additions & 0 deletions src/asm/asm.s
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,13 @@ _x86_64_asm_interrupt_disable:
cli
retq

.global _x86_64_asm_interrupt_enable_and_hlt
.p2align 4
_x86_64_asm_interrupt_enable_and_hlt:
sti
hlt
retq

.global _x86_64_asm_int3
.p2align 4
_x86_64_asm_int3:
Expand Down
6 changes: 6 additions & 0 deletions src/asm/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,12 @@ extern "C" {
)]
pub(crate) fn x86_64_asm_interrupt_disable();

#[cfg_attr(
any(target_env = "gnu", target_env = "musl"),
link_name = "_x86_64_asm_interrupt_enable_and_hlt"
)]
pub(crate) fn x86_64_asm_interrupt_enable_and_hlt();

#[cfg_attr(
any(target_env = "gnu", target_env = "musl"),
link_name = "_x86_64_asm_int3"
Expand Down
55 changes: 55 additions & 0 deletions src/instructions/interrupts.rs
Original file line number Diff line number Diff line change
Expand Up @@ -84,6 +84,61 @@ where
ret
}

/// Atomically enable interrupts and put the CPU to sleep
///
/// Executes the `sti; hlt` instruction sequence. Since the `sti` instruction
/// keeps interrupts disabled until after the immediately following
/// instruction (called "interrupt shadow"), no interrupt can occur between the
/// two instructions. (One exception to this are non-maskable interrupts; this
/// is explained below.)
///
/// This function is useful to put the CPU to sleep without missing interrupts
/// that occur immediately before the `hlt` instruction:
///
/// ```ignore
/// // there is a race between the check and the `hlt` instruction here:
///
/// if nothing_to_do() {
/// // <- race when the interrupt occurs here
/// x86_64::instructions::hlt(); // wait for the next interrupt
/// }
///
/// // avoid this race by using `enable_interrupts_and_hlt`:
///
/// x86_64::instructions::interrupts::disable();
/// if nothing_to_do() {
/// // <- no interrupts can occur here (interrupts are disabled)
/// x86_64::instructions::interrupts::enable_interrupts_and_hlt();
/// }
///
/// ```
///
/// ## Non-maskable Interrupts
///
/// On some processors, the interrupt shadow of `sti` does not apply to
/// non-maskable interrupts (NMIs). This means that an NMI can occur between
/// the `sti` and `hlt` instruction, with the result that the CPU is put to
/// sleep even though a new interrupt occured.
///
/// To work around this, it is recommended to check in the NMI handler if
/// the interrupt occured between `sti` and `hlt` instructions. If this is the
/// case, the handler should increase the instruction pointer stored in the
/// interrupt stack frame so that the `hlt` instruction is skipped.
///
/// See <http://lkml.iu.edu/hypermail/linux/kernel/1009.2/01406.html> for more
/// information.
#[inline]
pub fn enable_interrupts_and_hlt() {
#[cfg(feature = "inline_asm")]
unsafe {
asm!("sti; hlt" :::: "volatile");
}
#[cfg(not(feature = "inline_asm"))]
unsafe {
crate::asm::x86_64_asm_interrupt_enable_and_hlt();
}
}

/// Cause a breakpoint exception by invoking the `int3` instruction.
#[inline]
pub fn int3() {
Expand Down

0 comments on commit 944cf0e

Please sign in to comment.