ostd/irq/
level.rs

1// SPDX-License-Identifier: MPL-2.0
2
3//! The interrupt level.
4
5use crate::{cpu::PrivilegeLevel, cpu_local_cell};
6
7/// The current interrupt level on a CPU.
8///
9/// This type tracks the current nesting depth on the CPU
10/// where the code is executing.
11/// There are three levels:
12/// * Level 0 (the task context);
13/// * Level 1 (the interrupt context);
14/// * Level 2 (the interrupt context due to nested interrupts).
15///
16/// An `InterruptLevel` is specific to a single CPU
17/// and is meaningless when used by or sent to other CPUs,
18/// hence it is `!Send` and `!Sync`.
19#[derive(Copy, Clone, Debug, PartialEq, Eq)]
20pub enum InterruptLevel {
21    /// Level 0 (the task context).
22    L0,
23    /// Level 1 (the interrupt context).
24    ///
25    /// The intern value specifies the CPU privilege level of the interrupted code.
26    L1(PrivilegeLevel),
27    /// Level 2 (the interrupt context due to nested interrupts).
28    L2,
29}
30
31impl !Send for InterruptLevel {}
32impl !Sync for InterruptLevel {}
33
34impl InterruptLevel {
35    /// Returns the current interrupt level of this CPU.
36    pub fn current() -> Self {
37        // Parameters about the encoding of INTERRUPT_LEVEL
38        const LEVEL_VAL_OFFSET: u8 = 1;
39        const CPU_PRIV_MASK: u8 = 1 << 0;
40
41        let raw_level = INTERRUPT_LEVEL.load();
42        let level = raw_level >> LEVEL_VAL_OFFSET;
43        match level {
44            0 => Self::L0,
45            1 => {
46                let cpu_priv_at_irq = if (raw_level & CPU_PRIV_MASK) == 0 {
47                    PrivilegeLevel::Kernel
48                } else {
49                    PrivilegeLevel::User
50                };
51                Self::L1(cpu_priv_at_irq)
52            }
53            2 => Self::L2,
54            _ => unreachable!("level must between 0 and 2 (inclusive)"),
55        }
56    }
57
58    /// Returns the interrupt level as an integer between 0 and 2 (inclusive).
59    pub fn as_u8(&self) -> u8 {
60        match self {
61            Self::L0 => 0,
62            Self::L1(_) => 1,
63            Self::L2 => 2,
64        }
65    }
66
67    /// Checks if the CPU is currently in the task context (level 0).
68    pub fn is_task_context(&self) -> bool {
69        *self == Self::L0
70    }
71
72    /// Checks if the CPU is currently in the interrupt context (level 1 or 2).
73    pub fn is_interrupt_context(&self) -> bool {
74        matches!(self, Self::L1(_) | Self::L2)
75    }
76}
77
78/// Enters the scope of interrupt handling,
79/// increasing the interrupt level by one.
80///
81/// The `cpu_priv_at_irq` argument specifies the CPU privilege level of
82/// the code interrupted by the IRQ.
83pub(super) fn enter<F: FnOnce()>(f: F, cpu_priv_at_irq: PrivilegeLevel) {
84    let increment = {
85        let bit_0 = match cpu_priv_at_irq {
86            PrivilegeLevel::Kernel => 0,
87            PrivilegeLevel::User => 1,
88        };
89        let bit_1 = 0b10;
90        bit_1 | bit_0
91    };
92    INTERRUPT_LEVEL.add_assign(increment);
93
94    f();
95
96    INTERRUPT_LEVEL.sub_assign(increment);
97}
98
99cpu_local_cell! {
100    /// The interrupt level of the current IRQ.
101    ///
102    /// We pack two pieces of information into a single byte:
103    /// 1. The current interrupt level (bit 1 - 7);
104    /// 2. The CPU privilege level of the code interrupted by the IRQ (bit 0).
105    ///
106    /// More specifically,
107    /// the encoding of this byte is summarized in the table below.
108    ///
109    /// | Values   | Meaning             |
110    /// |----------|---------------------|
111    /// | `0b00_0` | L0                  |
112    /// | `0b01_0` | L1 from kernel      |
113    /// | `0b01_1` | L1 from user        |
114    /// | `0b10_0` | L2 (L1 from kernel) |
115    /// | `0b10_1` | L2 (L1 from user)   |
116    ///
117    /// This compact encoding allows us to update this value
118    /// in a single arithematic operation (see `enter`).
119    static INTERRUPT_LEVEL: u8 = 0;
120}