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}