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 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188
// SPDX-License-Identifier: MPL-2.0
#![allow(dead_code)]
use core::fmt::Debug;
use trapframe::TrapFrame;
use crate::{
arch::irq::{self, IrqCallbackHandle, IRQ_ALLOCATOR},
prelude::*,
task::{disable_preempt, DisablePreemptGuard},
Error,
};
/// Type alias for the irq callback function.
pub type IrqCallbackFunction = dyn Fn(&TrapFrame) + Sync + Send + 'static;
/// An Interrupt ReQuest(IRQ) line. User can use [`alloc`] or [`alloc_specific`] to get specific IRQ line.
///
/// The IRQ number is guaranteed to be external IRQ number and user can register callback functions to this IRQ resource.
/// When this resource is dropped, all the callback in this will be unregistered automatically.
///
/// [`alloc`]: Self::alloc
/// [`alloc_specific`]: Self::alloc_specific
#[derive(Debug)]
#[must_use]
pub struct IrqLine {
irq_num: u8,
#[allow(clippy::redundant_allocation)]
irq: Arc<&'static irq::IrqLine>,
callbacks: Vec<IrqCallbackHandle>,
}
impl IrqLine {
/// Allocates a specific IRQ line.
pub fn alloc_specific(irq: u8) -> Result<Self> {
IRQ_ALLOCATOR
.get()
.unwrap()
.lock()
.alloc_specific(irq as usize)
.map(|irq_num| Self::new(irq_num as u8))
.ok_or(Error::NotEnoughResources)
}
/// Allocates an available IRQ line.
pub fn alloc() -> Result<Self> {
let Some(irq_num) = IRQ_ALLOCATOR.get().unwrap().lock().alloc() else {
return Err(Error::NotEnoughResources);
};
Ok(Self::new(irq_num as u8))
}
fn new(irq_num: u8) -> Self {
// SAFETY: The IRQ number is allocated through `RecycleAllocator`, and it is guaranteed that the
// IRQ is not one of the important IRQ like cpu exception IRQ.
Self {
irq_num,
irq: unsafe { irq::IrqLine::acquire(irq_num) },
callbacks: Vec::new(),
}
}
/// Gets the IRQ number.
pub fn num(&self) -> u8 {
self.irq_num
}
/// Registers a callback that will be invoked when the IRQ is active.
///
/// For each IRQ line, multiple callbacks may be registered.
pub fn on_active<F>(&mut self, callback: F)
where
F: Fn(&TrapFrame) + Sync + Send + 'static,
{
self.callbacks.push(self.irq.on_active(callback))
}
/// Checks if there are no registered callbacks.
pub fn is_empty(&self) -> bool {
self.callbacks.is_empty()
}
}
impl Clone for IrqLine {
fn clone(&self) -> Self {
Self {
irq_num: self.irq_num,
irq: self.irq.clone(),
callbacks: Vec::new(),
}
}
}
impl Drop for IrqLine {
fn drop(&mut self) {
if Arc::strong_count(&self.irq) == 1 {
IRQ_ALLOCATOR
.get()
.unwrap()
.lock()
.free(self.irq_num as usize);
}
}
}
/// Disables all IRQs on the current CPU (i.e., locally).
///
/// This function returns a guard object, which will automatically enable local IRQs again when
/// it is dropped. This function works correctly even when it is called in a _nested_ way.
/// The local IRQs shall only be re-enabled when the most outer guard is dropped.
///
/// This function can play nicely with [`SpinLock`] as the type uses this function internally.
/// One can invoke this function even after acquiring a spin lock. And the reversed order is also ok.
///
/// [`SpinLock`]: crate::sync::SpinLock
///
/// # Example
///
/// ```rust
/// use ostd::irq;
///
/// {
/// let _ = irq::disable_local();
/// todo!("do something when irqs are disabled");
/// }
/// ```
#[must_use]
pub fn disable_local() -> DisabledLocalIrqGuard {
DisabledLocalIrqGuard::new()
}
/// A guard for disabled local IRQs.
pub struct DisabledLocalIrqGuard {
was_enabled: bool,
preempt_guard: DisablePreemptGuard,
}
impl !Send for DisabledLocalIrqGuard {}
impl DisabledLocalIrqGuard {
fn new() -> Self {
let was_enabled = irq::is_local_enabled();
if was_enabled {
irq::disable_local();
}
let preempt_guard = disable_preempt();
Self {
was_enabled,
preempt_guard,
}
}
/// Transfers the saved IRQ status of this guard to a new guard.
/// The saved IRQ status of this guard is cleared.
pub fn transfer_to(&mut self) -> Self {
let was_enabled = self.was_enabled;
self.was_enabled = false;
Self {
was_enabled,
preempt_guard: disable_preempt(),
}
}
}
impl Drop for DisabledLocalIrqGuard {
fn drop(&mut self) {
if self.was_enabled {
irq::enable_local();
}
}
}
/// Enables all IRQs on the current CPU.
///
/// FIXME: The reason we need to add this API is that currently IRQs
/// are enabled when the CPU enters the user space for the first time,
/// which is too late. During the OS initialization phase,
/// we need to get the block device working and mount the filesystems,
/// thus requiring the IRQs should be enabled as early as possible.
///
/// FIXME: this method may be unsound.
pub fn enable_local() {
if !crate::arch::irq::is_local_enabled() {
crate::arch::irq::enable_local();
}
}