ostd/irq/level.rs
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 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120
// SPDX-License-Identifier: MPL-2.0
//! The interrupt level.
use crate::{cpu::PrivilegeLevel, cpu_local_cell};
/// The current interrupt level on a CPU.
///
/// This type tracks the current nesting depth on the CPU
/// where the code is executing.
/// There are three levels:
/// * Level 0 (the task context);
/// * Level 1 (the interrupt context);
/// * Level 2 (the interrupt context due to nested interrupts).
///
/// An `InterruptLevel` is specific to a single CPU
/// and is meaningless when used by or sent to other CPUs,
/// hence it is `!Send` and `!Sync`.
#[derive(Copy, Clone, Debug, PartialEq, Eq)]
pub enum InterruptLevel {
/// Level 0 (the task context).
L0,
/// Level 1 (the interrupt context).
///
/// The intern value specifies the CPU privilege level of the interrupted code.
L1(PrivilegeLevel),
/// Level 2 (the interrupt context due to nested interrupts).
L2,
}
impl !Send for InterruptLevel {}
impl !Sync for InterruptLevel {}
impl InterruptLevel {
/// Returns the current interrupt level of this CPU.
pub fn current() -> Self {
// Parameters about the encoding of INTERRUPT_LEVEL
const LEVEL_VAL_OFFSET: u8 = 1;
const CPU_PRIV_MASK: u8 = 1 << 0;
let raw_level = INTERRUPT_LEVEL.load();
let level = raw_level >> LEVEL_VAL_OFFSET;
match level {
0 => Self::L0,
1 => {
let cpu_priv_at_irq = if (raw_level & CPU_PRIV_MASK) == 0 {
PrivilegeLevel::Kernel
} else {
PrivilegeLevel::User
};
Self::L1(cpu_priv_at_irq)
}
2 => Self::L2,
_ => unreachable!("level must between 0 and 2 (inclusive)"),
}
}
/// Returns the interrupt level as an integer between 0 and 2 (inclusive).
pub fn as_u8(&self) -> u8 {
match self {
Self::L0 => 0,
Self::L1(_) => 1,
Self::L2 => 2,
}
}
/// Checks if the CPU is currently in the task context (level 0).
pub fn is_task_context(&self) -> bool {
*self == Self::L0
}
/// Checks if the CPU is currently in the interrupt context (level 1 or 2).
pub fn is_interrupt_context(&self) -> bool {
matches!(self, Self::L1(_) | Self::L2)
}
}
/// Enters the scope of interrupt handling,
/// increasing the interrupt level by one.
///
/// The `cpu_priv_at_irq` argument specifies the CPU privilege level of
/// the code interrupted by the IRQ.
pub(super) fn enter<F: FnOnce()>(f: F, cpu_priv_at_irq: PrivilegeLevel) {
let increment = {
let bit_0 = match cpu_priv_at_irq {
PrivilegeLevel::Kernel => 0,
PrivilegeLevel::User => 1,
};
let bit_1 = 0b10;
bit_1 | bit_0
};
INTERRUPT_LEVEL.add_assign(increment);
f();
INTERRUPT_LEVEL.sub_assign(increment);
}
cpu_local_cell! {
/// The interrupt level of the current IRQ.
///
/// We pack two pieces of information into a single byte:
/// 1. The current interrupt level (bit 1 - 7);
/// 2. The CPU privilege level of the code interrupted by the IRQ (bit 0).
///
/// More specifically,
/// the encoding of this byte is summarized in the table below.
///
/// | Values | Meaning |
/// |----------|---------------------|
/// | `0b00_0` | L0 |
/// | `0b01_0` | L1 from kernel |
/// | `0b01_1` | L1 from user |
/// | `0b10_0` | L2 (L1 from kernel) |
/// | `0b10_1` | L2 (L1 from user) |
///
/// This compact encoding allows us to update this value
/// in a single arithematic operation (see `enter`).
static INTERRUPT_LEVEL: u8 = 0;
}