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
76
77
78
//! # Interrupt handling for x86.
//!
//! This module contains abstractions for configuring the programmable interrupt controllers:
//! - The legacy [pic] provides backward capabilities. On modern OSs, 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
//!
//!   +- Keyboard
//!   |
//! +-o------+
//! | IOAPIC | <- Redirection Table (Device -> Vector)
//! +---o----+
//!     |
//! ----oo-----... Interrupt Bus
//!      |
//! +----o----+
//! | LAPIC 0 | <- EOI, Timer, IPI, ...
//! +----o----+
//!      |
//!  +---o---+
//!  | CPU 0 |
//!  | - RIDT+---> IDT[Vector] => ISR
//!  +-------+
//!
//! ```

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 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)) };
}