DragonOS/kernel/src/exception/irqdomain.rs
LoGin bd70d2d1f4
chore: 将工具链更新到2024-07-23 (#864)
* chore: 将工具链更新到2024-07-23
2024-07-25 00:55:02 +08:00

701 lines
22 KiB
Rust
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

use core::fmt::Debug;
use alloc::{
string::{String, ToString},
sync::{Arc, Weak},
vec::Vec,
};
use hashbrown::HashMap;
use log::{info, warn};
use system_error::SystemError;
use crate::{
driver::{base::device::Device, open_firmware::device_node::DeviceNode},
exception::{irqdata::IrqLineStatus, irqdesc::irq_desc_manager, manage::irq_manager},
libs::{
rwlock::{RwLock, RwLockReadGuard, RwLockWriteGuard},
spinlock::SpinLock,
},
};
use super::{
dummychip::no_irq_chip,
irqchip::{IrqChip, IrqChipData, IrqChipGeneric, IrqGcFlags},
irqdata::{IrqData, IrqHandlerData},
irqdesc::{IrqDesc, IrqFlowHandler},
HardwareIrqNumber, IrqNumber,
};
static mut IRQ_DOMAIN_MANAGER: Option<Arc<IrqDomainManager>> = None;
/// 获取中断域管理器的引用
#[inline(always)]
pub fn irq_domain_manager() -> &'static Arc<IrqDomainManager> {
unsafe { IRQ_DOMAIN_MANAGER.as_ref().unwrap() }
}
pub(super) fn irq_domain_manager_init() {
unsafe {
IRQ_DOMAIN_MANAGER = Some(Arc::new(IrqDomainManager::new()));
}
}
/// 中断域管理器
pub struct IrqDomainManager {
domains: SpinLock<Vec<Arc<IrqDomain>>>,
inner: RwLock<InnerIrqDomainManager>,
}
impl IrqDomainManager {
pub fn new() -> IrqDomainManager {
IrqDomainManager {
domains: SpinLock::new(Vec::new()),
inner: RwLock::new(InnerIrqDomainManager {
default_domain: None,
}),
}
}
/// 创建一个新的线性映射的irqdomain, 并将其添加到irqdomain管理器中
///
/// 创建的irqdomain中断号是线性的即从0开始依次递增
///
/// ## 参数
///
/// - `name` - 中断域的名字
/// - `ops` - 中断域的操作
/// - `irq_size` - 中断号的数量
#[allow(dead_code)]
pub fn create_and_add_linear(
&self,
name: String,
ops: &'static dyn IrqDomainOps,
irq_size: u32,
) -> Option<Arc<IrqDomain>> {
self.create_and_add(
name,
ops,
IrqNumber::new(0),
HardwareIrqNumber::new(0),
irq_size,
)
}
/// 创建一个新的irqdomain, 并将其添加到irqdomain管理器中
///
/// ## 参数
///
/// - `name` - 中断域的名字
/// - `ops` - 中断域的操作
/// - `first_irq` - 起始软件中断号
/// - `first_hwirq` - 起始硬件中断号
/// - `irq_size` - 中断号的数量
///
/// 参考 https://code.dragonos.org.cn/xref/linux-6.1.9/kernel/irq/irqdomain.c?fi=__irq_domain_add#139
pub fn create_and_add(
&self,
name: String,
ops: &'static dyn IrqDomainOps,
first_irq: IrqNumber,
first_hwirq: HardwareIrqNumber,
irq_size: u32,
) -> Option<Arc<IrqDomain>> {
let domain = IrqDomain::new(
None,
Some(name),
ops,
IrqDomainFlags::NAME_ALLOCATED,
IrqDomainBusToken::Any,
first_irq + irq_size,
first_hwirq + irq_size,
)?;
self.add_domain(domain.clone());
return Some(domain);
}
fn add_domain(&self, domain: Arc<IrqDomain>) {
self.domains.lock_irqsave().push(domain);
}
#[allow(dead_code)]
pub fn remove_domain(&self, domain: &Arc<IrqDomain>) {
let mut domains = self.domains.lock_irqsave();
let index = domains
.iter()
.position(|x| Arc::ptr_eq(x, domain))
.expect("domain not found");
domains.remove(index);
}
/// 获取默认的中断域
#[allow(dead_code)]
pub fn default_domain(&self) -> Option<Arc<IrqDomain>> {
self.inner.read().default_domain.clone()
}
/// 设置默认的中断域
///
/// 在创建IRQ映射的时候如果没有指定中断域就会使用默认的中断域
pub fn set_default_domain(&self, domain: Arc<IrqDomain>) {
self.inner.write_irqsave().default_domain = Some(domain);
}
/// 将指定范围的硬件中断号与软件中断号一一对应的关联起来
///
/// ## 参数
///
/// - `domain` - 中断域
/// - `first_irq` - 起始软件中断号
/// - `first_hwirq` - 起始硬件中断号
/// - `count` - 数量
pub fn domain_associate_many(
&self,
domain: &Arc<IrqDomain>,
first_irq: IrqNumber,
first_hwirq: HardwareIrqNumber,
count: u32,
) {
for i in 0..count {
if let Err(e) = self.domain_associate(domain, first_irq + i, first_hwirq + i) {
warn!("domain associate failed: {:?}, domain '{:?}' didn't like hwirq {} to virq {} mapping.", e, domain.name(), (first_hwirq + i).data(), (first_irq + i).data());
}
}
}
/// 将一个硬件中断号与一个软件中断号关联起来
///
/// 参考 https://code.dragonos.org.cn/xref/linux-6.1.9/kernel/irq/irqdomain.c#562
pub fn domain_associate(
&self,
domain: &Arc<IrqDomain>,
irq: IrqNumber,
hwirq: HardwareIrqNumber,
) -> Result<(), SystemError> {
if hwirq >= domain.revmap.read_irqsave().hwirq_max {
warn!(
"hwirq {} is out of range for domain {:?}",
hwirq.data(),
domain.name()
);
return Err(SystemError::EINVAL);
}
let irq_data = irq_desc_manager()
.lookup(irq)
.ok_or_else(|| {
warn!("irq_desc not found for irq {}", irq.data());
SystemError::EINVAL
})?
.irq_data();
if irq_data.domain().is_some() {
warn!(
"irq {} is already associated with domain {:?}",
irq.data(),
irq_data.domain().unwrap().name()
);
return Err(SystemError::EINVAL);
}
let mut irq_data_guard = irq_data.inner();
irq_data_guard.set_hwirq(hwirq);
irq_data_guard.set_domain(Some(domain.clone()));
drop(irq_data_guard);
let r = domain.ops.map(domain, hwirq, irq);
if let Err(e) = r {
if e != SystemError::ENOSYS {
if e != SystemError::EPERM {
info!("domain associate failed: {:?}, domain '{:?}' didn't like hwirq {} to virq {} mapping.", e, domain.name(), hwirq.data(), irq.data());
}
let mut irq_data_guard = irq_data.inner();
irq_data_guard.set_domain(None);
irq_data_guard.set_hwirq(HardwareIrqNumber::new(0));
return Err(e);
}
}
if domain.name().is_none() {
let chip = irq_data.chip_info_read_irqsave().chip();
domain.set_name(chip.name().to_string());
}
self.irq_domain_set_mapping(domain, hwirq, irq_data);
irq_manager().irq_clear_status_flags(irq, IrqLineStatus::IRQ_NOREQUEST)?;
return Ok(());
}
fn irq_domain_set_mapping(
&self,
domain: &Arc<IrqDomain>,
hwirq: HardwareIrqNumber,
irq_data: Arc<IrqData>,
) {
if domain.no_map() {
return;
}
domain.revmap.write_irqsave().insert(hwirq, irq_data);
}
/// 递归调用 domain_ops->activate 以激活中断
///
/// ## 参数
///
/// - irq_data: 与中断关联的最外层 irq_data
/// - reserve: 如果为true则仅预留一个中断向量而不是分配一个
///
/// 这是调用 domain_ops->activate 以编程中断控制器的第二步,以便中断实际上可以被传递。
pub fn activate_irq(&self, irq_data: &Arc<IrqData>, reserve: bool) -> Result<(), SystemError> {
let mut r = Ok(());
// debug!(
// "activate_irq: irq_data.common_data().status().is_activated()={}",
// irq_data.common_data().status().is_activated()
// );
if !irq_data.common_data().status().is_activated() {
r = self.do_activate_irq(Some(irq_data.clone()), reserve);
}
if r.is_err() {
irq_data.common_data().status().set_activated();
}
return r;
}
#[inline(never)]
fn do_activate_irq(
&self,
irq_data: Option<Arc<IrqData>>,
reserve: bool,
) -> Result<(), SystemError> {
let mut r = Ok(());
if let Some(irq_data) = irq_data {
// debug!("do_activate_irq: irq_data={:?}", irq_data);
if let Some(domain) = irq_data.domain() {
// debug!("do_activate_irq: domain={:?}", domain.name());
let parent_data = irq_data.parent_data().and_then(|x| x.upgrade());
if let Some(parent_data) = parent_data.clone() {
r = self.do_activate_irq(Some(parent_data), reserve);
}
if r.is_err() {
let tmpr = domain.ops.activate(&domain, &irq_data, reserve);
if let Err(e) = tmpr {
if e != SystemError::ENOSYS && parent_data.is_some() {
self.do_deactivate_irq(parent_data);
}
}
}
}
}
return r;
}
#[allow(clippy::only_used_in_recursion)]
fn do_deactivate_irq(&self, irq_data: Option<Arc<IrqData>>) {
if let Some(irq_data) = irq_data {
if let Some(domain) = irq_data.domain() {
domain.ops.deactivate(&domain, &irq_data);
let pp = irq_data.parent_data().and_then(|x| x.upgrade());
if pp.is_some() {
self.do_deactivate_irq(pp);
}
}
}
}
/// `irq_domain_set_info` - 在 @domain 中为 @virq 设置完整的数据
///
/// ## 参数
///
/// - `domain`: 要匹配的中断域
/// - `virq`: IRQ号
/// - `hwirq`: 硬件中断号
/// - `chip`: 相关的中断芯片
/// - `chip_data`: 相关的中断芯片数据
/// - `handler`: 中断流处理器
/// - `handler_data`: 中断流处理程序数据
/// - `handler_name`: 中断处理程序名称
#[allow(clippy::too_many_arguments)]
pub fn domain_set_info(
&self,
domain: &Arc<IrqDomain>,
virq: IrqNumber,
hwirq: HardwareIrqNumber,
chip: Arc<dyn IrqChip>,
chip_data: Option<Arc<dyn IrqChipData>>,
flow_handler: &'static dyn IrqFlowHandler,
handler_data: Option<Arc<dyn IrqHandlerData>>,
handler_name: Option<String>,
) {
let r = self.domain_set_hwirq_and_chip(domain, virq, hwirq, Some(chip), chip_data);
if r.is_err() {
return;
}
irq_manager().__irq_set_handler(virq, flow_handler, false, handler_name);
irq_manager().irq_set_handler_data(virq, handler_data).ok();
}
/// `domain_set_hwirq_and_chip` - 在 @domain 中为 @virq 设置 hwirq 和 irqchip
///
/// ## 参数
///
/// - `domain`: 要匹配的中断域
/// - `virq`: IRQ号
/// - `hwirq`: hwirq号
/// - `chip`: 相关的中断芯片
/// - `chip_data`: 相关的芯片数据
pub fn domain_set_hwirq_and_chip(
&self,
domain: &Arc<IrqDomain>,
virq: IrqNumber,
hwirq: HardwareIrqNumber,
chip: Option<Arc<dyn IrqChip>>,
chip_data: Option<Arc<dyn IrqChipData>>,
) -> Result<(), SystemError> {
let irq_data: Arc<IrqData> = self
.domain_get_irq_data(domain, virq)
.ok_or(SystemError::ENOENT)?;
let mut inner = irq_data.inner();
let mut chip_info = irq_data.chip_info_write_irqsave();
inner.set_hwirq(hwirq);
if let Some(chip) = chip {
chip_info.set_chip(Some(chip));
} else {
chip_info.set_chip(Some(no_irq_chip()));
};
chip_info.set_chip_data(chip_data);
return Ok(());
}
/// `irq_domain_get_irq_data` - 获取与 @virq 和 @domain 关联的 irq_data
///
/// ## 参数
///
/// - `domain`: 要匹配的域
/// - `virq`: 要获取 irq_data 的IRQ号
pub fn domain_get_irq_data(
&self,
domain: &Arc<IrqDomain>,
virq: IrqNumber,
) -> Option<Arc<IrqData>> {
let desc = irq_desc_manager().lookup(virq)?;
let mut irq_data = Some(desc.irq_data());
while irq_data.is_some() {
let dt = irq_data.unwrap();
if dt.domain().is_some() && Arc::ptr_eq(dt.domain().as_ref().unwrap(), domain) {
return Some(dt);
}
irq_data = dt.parent_data().and_then(|x| x.upgrade());
}
return None;
}
/// `resolve_irq_mapping` - 从硬件中断号找到中断号。
///
/// ## 参数
///
/// - `domain`: 拥有此硬件中断的域
/// - `hwirq`: 该域空间中的硬件中断号
/// - `irq`: 如果需要可选的指针以返回Linux中断
///
/// ## 返回
///
/// 返回一个元组,包含中断描述符和中断号
pub fn resolve_irq_mapping(
&self,
mut domain: Option<Arc<IrqDomain>>,
hwirq: HardwareIrqNumber,
) -> Result<(Arc<IrqDesc>, IrqNumber), SystemError> {
if domain.is_none() {
domain = Some(self.default_domain().ok_or(SystemError::EINVAL)?);
}
let domain = domain.unwrap();
if domain.no_map() {
if hwirq < domain.revmap_read_irqsave().hwirq_max {
let irq_desc = irq_desc_manager()
.lookup(IrqNumber::new(hwirq.data()))
.ok_or(SystemError::EINVAL)?;
if irq_desc.irq_data().hardware_irq() == hwirq {
let irq = irq_desc.irq_data().irq();
return Ok((irq_desc, irq));
}
}
return Err(SystemError::EINVAL);
}
let revmap = domain.revmap_read_irqsave();
let irq_data = revmap.lookup(hwirq).ok_or(SystemError::EINVAL)?;
let irq_desc = irq_data.irq_desc().unwrap();
return Ok((irq_desc, irq_data.irq()));
}
}
struct InnerIrqDomainManager {
default_domain: Option<Arc<IrqDomain>>,
}
/// 中断域
///
/// 用于把硬件中断号翻译为软件中断号的映射的对象
///
/// 参考 https://code.dragonos.org.cn/xref/linux-6.1.9/include/linux/irqdomain.h#164
#[allow(dead_code)]
#[derive(Debug)]
pub struct IrqDomain {
/// 中断域的名字 (二选一)
name: Option<&'static str>,
allocated_name: SpinLock<Option<String>>,
/// 中断域的操作
ops: &'static dyn IrqDomainOps,
inner: SpinLock<InnerIrqDomain>,
/// 中断号反向映射
revmap: RwLock<IrqDomainRevMap>,
}
#[allow(dead_code)]
#[derive(Debug)]
struct InnerIrqDomain {
/// this field not touched by the core code
host_data: Option<Arc<dyn IrqChipData>>,
/// host per irq_domain flags
flags: IrqDomainFlags,
/// The number of mapped interrupts
mapcount: u32,
bus_token: IrqDomainBusToken,
/// 指向 generic chip 列表的指针。
/// 有一个辅助函数用于为中断控制器驱动程序设置一个或
/// 多个 generic chip该函数使用此指针并依赖于 generic chip 库。
generic_chip: Option<Arc<IrqDomainChipGeneric>>,
/// Pointer to a device that the domain represent, and that will be
/// used for power management purposes.
device: Option<Arc<dyn Device>>,
/// Pointer to parent irq_domain to support hierarchy irq_domains
parent: Option<Weak<IrqDomain>>,
}
impl IrqDomain {
#[allow(dead_code)]
pub fn new(
name: Option<&'static str>,
allocated_name: Option<String>,
ops: &'static dyn IrqDomainOps,
flags: IrqDomainFlags,
bus_token: IrqDomainBusToken,
irq_max: IrqNumber,
hwirq_max: HardwareIrqNumber,
) -> Option<Arc<Self>> {
if name.is_none() && allocated_name.is_none() {
return None;
}
let x = IrqDomain {
name,
allocated_name: SpinLock::new(allocated_name),
ops,
inner: SpinLock::new(InnerIrqDomain {
host_data: None,
flags,
mapcount: 0,
bus_token,
generic_chip: None,
device: None,
parent: None,
}),
revmap: RwLock::new(IrqDomainRevMap {
map: HashMap::new(),
hwirq_max,
irq_max,
}),
};
return Some(Arc::new(x));
}
/// 中断域是否不对中断号进行转换
pub fn no_map(&self) -> bool {
self.inner
.lock_irqsave()
.flags
.contains(IrqDomainFlags::NO_MAP)
}
#[allow(dead_code)]
fn revmap_read_irqsave(&self) -> RwLockReadGuard<IrqDomainRevMap> {
self.revmap.read_irqsave()
}
#[allow(dead_code)]
fn revmap_write_irqsave(&self) -> RwLockWriteGuard<IrqDomainRevMap> {
self.revmap.write_irqsave()
}
#[allow(dead_code)]
fn set_hwirq_max(&self, hwirq_max: HardwareIrqNumber) {
self.revmap_write_irqsave().hwirq_max = hwirq_max;
}
pub fn name(&self) -> Option<String> {
if let Some(name) = self.name {
return Some(name.to_string());
}
return self.allocated_name.lock_irqsave().clone();
}
pub fn set_name(&self, name: String) {
*self.allocated_name.lock_irqsave() = Some(name);
}
/// The number of mapped interrupts
pub fn map_count(&self) -> u32 {
self.revmap_read_irqsave().map.len() as u32
}
pub fn host_data(&self) -> Option<Arc<dyn IrqChipData>> {
self.inner.lock_irqsave().host_data.clone()
}
pub fn set_host_data(&self, host_data: Option<Arc<dyn IrqChipData>>) {
self.inner.lock_irqsave().host_data = host_data;
}
}
/// 参考 https://code.dragonos.org.cn/xref/linux-6.1.9/include/linux/irqdomain.h#190
#[allow(dead_code)]
#[derive(Debug)]
struct IrqDomainRevMap {
map: HashMap<HardwareIrqNumber, Arc<IrqData>>,
hwirq_max: HardwareIrqNumber,
irq_max: IrqNumber,
}
impl IrqDomainRevMap {
fn insert(&mut self, hwirq: HardwareIrqNumber, irq_data: Arc<IrqData>) {
self.map.insert(hwirq, irq_data);
}
#[allow(dead_code)]
fn remove(&mut self, hwirq: HardwareIrqNumber) {
self.map.remove(&hwirq);
}
#[allow(dead_code)]
fn lookup(&self, hwirq: HardwareIrqNumber) -> Option<Arc<IrqData>> {
self.map.get(&hwirq).cloned()
}
}
bitflags! {
pub struct IrqDomainFlags: u32 {
/// Irq domain is hierarchical
const HIERARCHY = (1 << 0);
/// Irq domain name was allocated dynamically
const NAME_ALLOCATED = (1 << 1);
/// Irq domain is an IPI domain with virq per cpu
const IPI_PER_CPU = (1 << 2);
/// Irq domain is an IPI domain with single virq
const IPI_SINGLE = (1 << 3);
/// Irq domain implements MSIs
const MSI = (1 << 4);
/// Irq domain implements MSI remapping
const MSI_REMAP = (1 << 5);
/// Quirk to handle MSI implementations which do not provide masking
const MSI_NOMASK_QUIRK = (1 << 6);
/// Irq domain doesn't translate anything
const NO_MAP = (1 << 7);
/// Flags starting from IRQ_DOMAIN_FLAG_NONCORE are reserved
/// for implementation specific purposes and ignored by the core code
const NONCORE = (1 << 16);
}
}
/// 如果多个域有相同的设备节点但服务于不同的目的例如一个域用于PCI/MSI另一个用于有线IRQs
/// 它们可以使用特定于总线的token进行区分。预计大多数域只会携带`DomainBusAny`。
///
/// 参考 https://code.dragonos.org.cn/xref/linux-6.1.9/include/linux/irqdomain.h#78
#[allow(dead_code)]
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum IrqDomainBusToken {
Any = 0,
Wired,
GenericMsi,
PciMsi,
PlatformMsi,
Nexus,
Ipi,
FslMcMsi,
TiSciIntaMsi,
Wakeup,
VmdMsi,
}
/// IrqDomain的操作方法
///
/// 参考 https://code.dragonos.org.cn/xref/linux-6.1.9/include/linux/irqdomain.h#107
pub trait IrqDomainOps: Debug + Send + Sync {
/// 匹配一个中断控制器设备节点到一个主机。
#[allow(dead_code)]
fn match_node(
&self,
_irq_domain: &Arc<IrqDomain>,
_device_node: &Arc<DeviceNode>,
_bus_token: IrqDomainBusToken,
) -> bool {
false
}
/// 创建或更新一个虚拟中断号与一个硬件中断号之间的映射。
/// 对于给定的映射,这只会被调用一次。
///
/// 如果没有实现这个方法,那么就会返回`ENOSYS`
fn map(
&self,
_irq_domain: &Arc<IrqDomain>,
_hwirq: HardwareIrqNumber,
_virq: IrqNumber,
) -> Result<(), SystemError> {
Err(SystemError::ENOSYS)
}
/// 删除一个虚拟中断号与一个硬件中断号之间的映射。
#[allow(dead_code)]
fn unmap(&self, irq_domain: &Arc<IrqDomain>, virq: IrqNumber);
fn activate(
&self,
_domain: &Arc<IrqDomain>,
_irq_data: &Arc<IrqData>,
_reserve: bool,
) -> Result<(), SystemError> {
Err(SystemError::ENOSYS)
}
fn deactivate(&self, _domain: &Arc<IrqDomain>, _irq_data: &Arc<IrqData>) {}
}
#[allow(dead_code)]
#[derive(Debug)]
pub struct IrqDomainChipGeneric {
inner: SpinLock<InnerIrqDomainChipGeneric>,
}
#[allow(dead_code)]
#[derive(Debug)]
struct InnerIrqDomainChipGeneric {
irqs_per_chip: u32,
flags_to_clear: IrqGcFlags,
flags_to_set: IrqGcFlags,
gc_flags: IrqGcFlags,
gc: Vec<Arc<IrqChipGeneric>>,
}