1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
//! # Interrupt handling for x86.
//!
//! This module contains abstractions for configuring the programmable interrupt controllers:
//! - The legacy [pic] provides backward capabilities. On modern OSes, it is not used anymore and must be disabled when using the modern APICs.
//! - The modern "advanced" [ioapic] and [lapic] controllers are more capable but also more difficult to configure. The former (IOAPIC) is shared between all cores and the latter (LAPIC) exists for each core.
//!
//! These interrupt controllers are configured together with the
//! interrupt descriptor table ([idt]), which is used to define the
//! interrupt-handler functions that are executed for a given interrupt vector.
//!
//! ## Overview
//!
//! The ASCII art below show the way a keyboard interrupt has to undergo to
//! reach the CPU and the configured interrupt service routine on x86.
//!
//! ```text
//! +---------+
//! | CPU 0   |
//! |         |
//! |   +-RIDT----> IDT[Vector] => handler
//! +---|-----+
//! | LAPIC 0 | <- EOI, Timer, IPI, ...
//! +----o----+
//!      |
//! ---o-o------- ... Interrupt Bus
//!    |
//!  +-o------+
//!  | IOAPIC | <- Redirection Table
//!  +--o---o-+    (Device -> Vector)
//!    /     \
//! Keyboard  HDD ...
//! ```

use core::arch::asm;

use super::regs::Flags;

pub mod apic;
#[macro_use]
pub mod idt;
pub mod ioapic;
pub mod lapic;
pub mod pic;

/// Suppresses interrupts during the execution of `f` and
/// restore the previous status afterwards.
pub fn suppress<R>(f: impl FnOnce() -> R) -> R {
    let enabled = enabled();
    enable(false);
    let ret = f();
    enable(enabled);
    ret
}

/// Return if interrupts are currently enabled.
pub fn enabled() -> bool {
    Flags::read().interrupt()
}

/// Enable or disable interrupts.
pub fn enable(enable: bool) {
    if enable {
        unsafe { asm!("sti", options(nomem, nostack)) };
    } else {
        unsafe { asm!("cli", options(nomem, nostack)) };
    }
}

/// Enable interrupts and halt execution.
///
/// These two instructions are executed atomically, preventing race conditions,
/// where an interrupt in between is missed.
pub fn idle() {
    unsafe { asm!("sti; hlt", options(nomem, nostack)) };
}