From ce5850adbf74ec6c6717bbb5b1749f1fbff4ca0d Mon Sep 17 00:00:00 2001 From: LoGin Date: Sun, 18 Feb 2024 20:41:41 +0800 Subject: [PATCH] =?UTF-8?q?=E6=B7=BB=E5=8A=A0irqchip=E8=BF=99=E4=B8=80?= =?UTF-8?q?=E5=B1=82=E7=9A=84=E6=95=B0=E6=8D=AE=E7=BB=93=E6=9E=84(?= =?UTF-8?q?=E5=B0=9A=E6=9C=AA=E6=8E=A5=E5=85=A5=E7=9C=9F=E5=AE=9E=E7=9A=84?= =?UTF-8?q?=E8=8A=AF=E7=89=87)=20(#520)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * 添加irqchip这一层的数据结构(尚未接入真实的芯片) --- kernel/src/driver/net/e1000e/e1000e.rs | 4 +- .../src/driver/open_firmware/device_node.rs | 177 ++++++++++ kernel/src/driver/open_firmware/mod.rs | 1 + kernel/src/driver/pci/pci_irq.rs | 8 +- kernel/src/driver/virtio/transport_pci.rs | 4 +- kernel/src/exception/irqchip.rs | 239 +++++++++++++ kernel/src/exception/irqdata.rs | 332 ++++++++++++++++++ kernel/src/exception/irqdomain.rs | 184 ++++++++++ kernel/src/exception/mod.rs | 12 + kernel/src/exception/msi.rs | 126 +++++++ 10 files changed, 1079 insertions(+), 8 deletions(-) create mode 100644 kernel/src/driver/open_firmware/device_node.rs create mode 100644 kernel/src/exception/irqchip.rs create mode 100644 kernel/src/exception/irqdata.rs create mode 100644 kernel/src/exception/irqdomain.rs create mode 100644 kernel/src/exception/msi.rs diff --git a/kernel/src/driver/net/e1000e/e1000e.rs b/kernel/src/driver/net/e1000e/e1000e.rs index 9d061df4..21e4ee4a 100644 --- a/kernel/src/driver/net/e1000e/e1000e.rs +++ b/kernel/src/driver/net/e1000e/e1000e.rs @@ -14,7 +14,7 @@ use crate::driver::pci::pci::{ get_pci_device_structure_mut, PciDeviceStructure, PciDeviceStructureGeneralDevice, PciError, PCI_DEVICE_LINKEDLIST, }; -use crate::driver::pci::pci_irq::{IrqCommonMsg, IrqMsg, IrqSpecificMsg, PciInterrupt, IRQ}; +use crate::driver::pci::pci_irq::{IrqCommonMsg, IrqSpecificMsg, PciInterrupt, PciIrqMsg, IRQ}; use crate::include::bindings::bindings::pt_regs; use crate::libs::volatile::{ReadOnly, Volatile, WriteOnly}; use crate::net::net_core::poll_ifaces_try_lock_onetime; @@ -227,7 +227,7 @@ impl E1000EDevice { let irq_vector = device.irq_vector_mut().unwrap(); irq_vector.push(E1000E_RECV_VECTOR); device.irq_init(IRQ::PCI_IRQ_MSI).expect("IRQ Init Failed"); - let msg = IrqMsg { + let msg = PciIrqMsg { irq_common_message: IrqCommonMsg::init_from( 0, "E1000E_RECV_IRQ", diff --git a/kernel/src/driver/open_firmware/device_node.rs b/kernel/src/driver/open_firmware/device_node.rs new file mode 100644 index 00000000..808806c3 --- /dev/null +++ b/kernel/src/driver/open_firmware/device_node.rs @@ -0,0 +1,177 @@ +use crate::{ + driver::base::{ + kobject::{KObjType, KObject, KObjectState}, + kset::KSet, + }, + filesystem::{kernfs::KernFSInode, sysfs::BinAttribute}, + libs::rwlock::{RwLockReadGuard, RwLockWriteGuard}, + libs::spinlock::SpinLock, +}; +use alloc::{ + string::String, + sync::{Arc, Weak}, + vec::Vec, +}; +use core::fmt::Debug; + +/// 参考 https://code.dragonos.org.cn/xref/linux-6.1.9/include/linux/of.h#51 +#[allow(dead_code)] +#[derive(Debug)] +pub struct DeviceNode { + full_name: Option<&'static str>, + full_name_allocated: Option, + inner: SpinLock, +} + +#[allow(dead_code)] +#[derive(Debug)] +struct InnerDeviceNode { + properties: Vec, + parent: Weak, + children: Vec>, + sibling: Option>, + private_data: Option>, +} + +#[allow(dead_code)] +impl DeviceNode { + pub fn new( + full_name: Option<&'static str>, + full_name_allocated: Option, + ) -> Option> { + if full_name.is_none() && full_name_allocated.is_none() { + return None; + } + + let x = DeviceNode { + full_name, + full_name_allocated, + inner: SpinLock::new(InnerDeviceNode { + properties: Vec::new(), + parent: Weak::new(), + children: Vec::new(), + sibling: None, + private_data: None, + }), + }; + + return Some(Arc::new(x)); + } + + pub fn add_property(&self, prop: Property) { + self.inner.lock().properties.push(prop); + } + + pub fn properties(&self) -> Vec { + self.inner.lock().properties.clone() + } + + pub fn parent(&self) -> Option> { + self.inner.lock().parent.upgrade() + } + + pub fn set_parent(&self, parent: Arc) { + self.inner.lock().parent = Arc::downgrade(&parent); + } + + pub fn children(&self) -> Vec> { + self.inner.lock().children.clone() + } + + pub fn add_child(&self, child: Arc) { + self.inner.lock().children.push(child); + } + + pub fn sibling(&self) -> Option> { + self.inner.lock().sibling.as_ref().and_then(|s| s.upgrade()) + } + + pub fn set_sibling(&self, sibling: Arc) { + self.inner.lock().sibling = Some(Arc::downgrade(&sibling)); + } + + pub fn private_data(&self) -> Option> { + self.inner.lock().private_data.clone() + } + + pub fn set_private_data(&self, data: Arc) { + self.inner.lock().private_data = Some(data); + } +} + +pub trait DeviceNodePrivateData: Send + Sync + Debug {} + +impl KObject for DeviceNode { + fn as_any_ref(&self) -> &dyn core::any::Any { + self + } + + fn set_inode(&self, _inode: Option>) { + todo!() + } + + fn inode(&self) -> Option> { + todo!() + } + + fn parent(&self) -> Option> { + todo!() + } + + fn set_parent(&self, _parent: Option>) { + todo!() + } + + fn kset(&self) -> Option> { + todo!() + } + + fn set_kset(&self, _kset: Option>) { + todo!() + } + + fn kobj_type(&self) -> Option<&'static dyn KObjType> { + todo!() + } + + fn set_kobj_type(&self, _ktype: Option<&'static dyn KObjType>) { + todo!() + } + + fn name(&self) -> String { + todo!() + } + + fn set_name(&self, _name: String) {} + + fn kobj_state(&self) -> RwLockReadGuard { + todo!() + } + + fn kobj_state_mut(&self) -> RwLockWriteGuard { + todo!() + } + + fn set_kobj_state(&self, _state: KObjectState) { + todo!() + } +} + +#[allow(dead_code)] +#[derive(Debug, Clone)] +pub struct Property { + name: String, + value: Vec, + bin_attr: Option>, +} + +impl Property { + #[allow(dead_code)] + pub const fn new(name: String, value: Vec, battr: Option>) -> Self { + Property { + name, + value, + bin_attr: battr, + } + } +} diff --git a/kernel/src/driver/open_firmware/mod.rs b/kernel/src/driver/open_firmware/mod.rs index 8492a059..62673507 100644 --- a/kernel/src/driver/open_firmware/mod.rs +++ b/kernel/src/driver/open_firmware/mod.rs @@ -1,2 +1,3 @@ // #[cfg(target_arch = "riscv64")] +pub mod device_node; pub mod fdt; diff --git a/kernel/src/driver/pci/pci_irq.rs b/kernel/src/driver/pci/pci_irq.rs index 240e91ad..d0a2ed7f 100644 --- a/kernel/src/driver/pci/pci_irq.rs +++ b/kernel/src/driver/pci/pci_irq.rs @@ -70,7 +70,7 @@ pub enum IrqType { // PCI设备install中断时需要传递的参数 #[derive(Clone, Debug)] -pub struct IrqMsg { +pub struct PciIrqMsg { pub irq_common_message: IrqCommonMsg, pub irq_specific_message: IrqSpecificMsg, } @@ -301,7 +301,7 @@ pub trait PciInterrupt: PciDeviceStructure { /// @param self PCI设备的可变引用 /// @param msg PCI设备install中断时需要传递的共同参数 /// @return 一切正常返回Ok(0),有错误返回对应错误原因 - fn irq_install(&mut self, msg: IrqMsg) -> Result { + fn irq_install(&mut self, msg: PciIrqMsg) -> Result { if let Some(irq_vector) = self.irq_vector_mut() { if msg.irq_common_message.irq_index as usize > irq_vector.len() { return Err(PciError::PciIrqError(PciIrqError::InvalidIrqIndex( @@ -332,7 +332,7 @@ pub trait PciInterrupt: PciDeviceStructure { /// @param self PCI设备的可变引用 /// @param msg PCI设备install中断时需要传递的共同参数 /// @return 一切正常返回Ok(0),有错误返回对应错误原因 - fn msi_install(&mut self, msg: IrqMsg) -> Result { + fn msi_install(&mut self, msg: PciIrqMsg) -> Result { if let Some(irq_type) = self.irq_type_mut() { match *irq_type { IrqType::Msi { @@ -482,7 +482,7 @@ pub trait PciInterrupt: PciDeviceStructure { /// @param self PCI设备的可变引用 /// @param msg PCI设备install中断时需要传递的共同参数 /// @return 一切正常返回Ok(0),有错误返回对应错误原因 - fn msix_install(&mut self, msg: IrqMsg) -> Result { + fn msix_install(&mut self, msg: PciIrqMsg) -> Result { if let Some(irq_type) = self.irq_type_mut() { match *irq_type { IrqType::Msix { diff --git a/kernel/src/driver/virtio/transport_pci.rs b/kernel/src/driver/virtio/transport_pci.rs index 79a58e71..2e793c58 100644 --- a/kernel/src/driver/virtio/transport_pci.rs +++ b/kernel/src/driver/virtio/transport_pci.rs @@ -5,7 +5,7 @@ use crate::driver::pci::pci::{ PciStandardDeviceBar, PCI_CAP_ID_VNDR, }; -use crate::driver::pci::pci_irq::{IrqCommonMsg, IrqMsg, IrqSpecificMsg, PciInterrupt, IRQ}; +use crate::driver::pci::pci_irq::{IrqCommonMsg, IrqSpecificMsg, PciInterrupt, PciIrqMsg, IRQ}; use crate::include::bindings::bindings::pt_regs; use crate::libs::volatile::{ volread, volwrite, ReadOnly, Volatile, VolatileReadable, VolatileWritable, WriteOnly, @@ -133,7 +133,7 @@ impl PciTransport { .irq_init(IRQ::PCI_IRQ_MSIX) .expect("IRQ init failed"); // 中断相关信息 - let msg = IrqMsg { + let msg = PciIrqMsg { irq_common_message: IrqCommonMsg::init_from( 0, "Virtio_Recv_IRQ", diff --git a/kernel/src/exception/irqchip.rs b/kernel/src/exception/irqchip.rs new file mode 100644 index 00000000..9f98e931 --- /dev/null +++ b/kernel/src/exception/irqchip.rs @@ -0,0 +1,239 @@ +use core::{any::Any, fmt::Debug}; + +use alloc::{ + sync::{Arc, Weak}, + vec::Vec, +}; +use system_error::SystemError; + +use crate::{libs::spinlock::SpinLock, mm::VirtAddr}; + +use super::{ + irqdata::{IrqData, IrqLineStatus}, + irqdomain::IrqDomain, + msi::MsiMsg, +}; + +/// 参考 https://code.dragonos.org.cn/xref/linux-6.1.9/include/linux/irq.h#506 +pub trait IrqChip: Sync + Send + Any + Debug { + fn name(&self) -> &'static str; + /// start up the interrupt (defaults to ->enable if ENOSYS) + fn irq_startup(&self, _irq: &Arc) -> Result<(), SystemError> { + Err(SystemError::ENOSYS) + } + + /// shut down the interrupt (defaults to ->disable if ENOSYS) + fn irq_shutdown(&self, _irq: &Arc) -> Result<(), SystemError> { + Err(SystemError::ENOSYS) + } + + /// enable the interrupt + /// + /// (defaults to ->unmask if ENOSYS) + fn irq_enable(&self, _irq: &Arc) -> Result<(), SystemError> { + Err(SystemError::ENOSYS) + } + + /// disable the interrupt + fn irq_disable(&self, irq: &Arc); + + /// start of a new interrupt + fn irq_ack(&self, irq: &Arc); + + /// mask an interrupt source + fn irq_mask(&self, irq: &Arc); + /// ack and mask an interrupt source + fn irq_mask_ack(&self, irq: &Arc); + /// unmask an interrupt source + fn irq_unmask(&self, irq: &Arc); + /// end of interrupt + fn irq_eoi(&self, irq: &Arc); + + // todo: set affinity + + /// retrigger an IRQ to the CPU + fn retrigger(&self, irq: &Arc); + + /// set the flow type of an interrupt + /// + /// flow_type: the flow type to set + /// + fn irq_set_type( + &self, + _irq: &Arc, + _flow_type: IrqLineStatus, + ) -> Result<(), SystemError> { + Err(SystemError::ENOSYS) + } + + /// enable/disable power management wake-on of an interrupt + fn irq_set_wake(&self, _irq: &Arc, _on: bool) -> Result<(), SystemError> { + Err(SystemError::ENOSYS) + } + + /// function to lock access to slow bus (i2c) chips + fn irq_bus_lock(&self, _irq: &Arc) -> Result<(), SystemError> { + Ok(()) + } + + /// function to sync and unlock slow bus (i2c) chips + fn irq_bus_sync_unlock(&self, _irq: &Arc) -> Result<(), SystemError> { + Ok(()) + } + + /// function called from core code on suspend once per + /// chip, when one or more interrupts are installed + fn irq_suspend(&self, irq: &Arc); + + /// function called from core code on resume once per chip, + /// when one ore more interrupts are installed + fn irq_resume(&self, irq: &Arc); + + /// function called from core code on shutdown once per chip + fn irq_pm_shutdown(&self, irq: &Arc); + + /// Optional function to set irq_data.mask for special cases + fn irq_calc_mask(&self, _irq: &Arc) {} + + // todo: print chip + + /// optional to request resources before calling + /// any other callback related to this irq + fn irq_request_resources(&self, _irq: &Arc) -> Result<(), SystemError> { + Ok(()) + } + + /// optional to release resources acquired with + /// irq_request_resources + fn irq_release_resources(&self, _irq: &Arc) {} + + /// optional to compose message content for MSI + fn irq_compose_msi_msg(&self, _irq: &Arc, _msg: &mut MsiMsg) {} + + /// optional to write message content for MSI + fn irq_write_msi_msg(&self, _irq: &Arc, _msg: &MsiMsg) {} + + /// return the internal state of an interrupt + fn irqchip_state( + &self, + _irq: &Arc, + _which: IrqChipState, + ) -> Result { + Err(SystemError::ENOSYS) + } + + /// set the internal state of an interrupt + fn set_irqchip_state( + &self, + _irq: &Arc, + _which: IrqChipState, + _state: bool, + ) -> Result<(), SystemError> { + Err(SystemError::ENOSYS) + } + + // todo: set vcpu affinity + + /// send a single IPI to destination cpus + fn send_single_ipi(&self, irq: &Arc, cpu: u32); + + // todo: send ipi with cpu mask + + /// function called from core code before enabling an NMI + fn irq_nmi_setup(&self, _irq: &Arc) -> Result<(), SystemError> { + Err(SystemError::ENOSYS) + } + + /// function called from core code after disabling an NMI + fn irq_nmi_teardown(&self, irq: &Arc); +} + +#[allow(dead_code)] +#[derive(Debug, Clone, Copy)] +pub enum IrqChipState { + /// Is the interrupt pending? + Pending, + /// Is the interrupt in progress? + Active, + /// Is the interrupt masked? + Masked, + /// Is Irq line high? + LineLevel, +} + +pub trait IrqChipData: Sync + Send + Any + Debug {} + +bitflags! { + /// 定义 IrqGcFlags 位标志 + pub struct IrqGcFlags: u32 { + /// 通过读取mask reg来初始化mask_cache + const IRQ_GC_INIT_MASK_CACHE = 1 << 0; + /// 对于需要在父irq上调用irq_set_wake()的irq芯片, 将irqs的锁类设置为嵌套。Usually GPIO implementations + const IRQ_GC_INIT_NESTED_LOCK = 1 << 1; + /// Mask cache是芯片类型私有的 + const IRQ_GC_MASK_CACHE_PER_TYPE = 1 << 2; + /// 不计算irqData->mask + const IRQ_GC_NO_MASK = 1 << 3; + /// 使用大端字节序的寄存器访问(默认:小端LE) + const IRQ_GC_BE_IO = 1 << 4; + } +} + +#[allow(dead_code)] +#[derive(Debug)] +pub struct IrqChipGeneric { + inner: SpinLock, +} + +#[allow(dead_code)] +#[derive(Debug)] +struct InnerIrqChipGeneric { + /// Register base address + reg_base: VirtAddr, + ops: &'static dyn IrqChipGenericOps, + /// Interrupt base num for this chip + irq_base: u32, + /// Number of interrupts handled by this chip + irq_cnt: u32, + /// Cached mask register shared between all chip types + mask_cache: u32, + /// Cached type register + type_cache: u32, + /// Cached polarity register + polarity_cache: u32, + /// Interrupt can wakeup from suspend + wake_enabled: bool, + /// Interrupt is marked as an wakeup from suspend source + wake_active: bool, + /// Number of available irq_chip_type instances (usually 1) + num_chip_type: u32, + private_data: Option>, + installed: u64, + unused: u64, + domain: Weak, + chip_types: Vec, +} + +pub trait IrqChipGenericOps: Debug { + /// Alternate I/O accessor (defaults to readl if NULL) + unsafe fn reg_readl(&self, addr: VirtAddr) -> u32; + + /// Alternate I/O accessor (defaults to writel if NULL) + unsafe fn reg_writel(&self, addr: VirtAddr, val: u32); + + /// Function called from core code on suspend once per + /// chip; can be useful instead of irq_chip::suspend to + /// handle chip details even when no interrupts are in use + fn suspend(&self, gc: &Arc); + /// Function called from core code on resume once per chip; + /// can be useful instead of irq_chip::resume to handle chip + /// details even when no interrupts are in use + fn resume(&self, gc: &Arc); +} + +pub trait IrqChipGenericPrivateData: Sync + Send + Any + Debug {} + +#[derive(Debug)] +pub struct IrqChipType { + // todo https://code.dragonos.org.cn/xref/linux-6.1.9/include/linux/irq.h#1024 +} diff --git a/kernel/src/exception/irqdata.rs b/kernel/src/exception/irqdata.rs new file mode 100644 index 00000000..8d18dcb6 --- /dev/null +++ b/kernel/src/exception/irqdata.rs @@ -0,0 +1,332 @@ +use core::{any::Any, fmt::Debug}; + +use alloc::sync::{Arc, Weak}; + +use crate::libs::spinlock::SpinLock; + +use super::{ + irqchip::{IrqChip, IrqChipData}, + irqdomain::IrqDomain, + msi::MsiDesc, + HardwareIrqNumber, IrqNumber, +}; + +/// per irq chip data passed down to chip functions +/// +/// 该结构体用于表示每个Irq的私有数据,且与具体的中断芯片绑定 +/// +/// 参考: https://code.dragonos.org.cn/xref/linux-6.1.9/include/linux/irq.h#179 +#[allow(dead_code)] +#[derive(Debug)] +pub struct IrqData { + /// 中断号, 用于表示软件逻辑视角的中断号,全局唯一 + irq: IrqNumber, + /// 硬件中断号, 用于表示在某个IrqDomain中的中断号 + hwirq: HardwareIrqNumber, + /// 涉及的所有irqchip之间共享的数据 + common_data: Arc, + /// 绑定到的中断芯片 + chip: Arc, + /// 中断芯片的私有数据(与当前irq相关) + chip_data: Arc, + /// 中断域 + domain: Arc, + /// 中断的父中断(如果具有中断域继承的话) + parent_data: Option>, +} + +/// per irq data shared by all irqchips +/// +/// 参考 https://code.dragonos.org.cn/xref/linux-6.1.9/include/linux/irq.h#147 +#[allow(dead_code)] +#[derive(Debug)] +pub struct IrqCommonData { + inner: SpinLock, +} + +#[allow(dead_code)] +#[derive(Debug)] +struct InnerIrqCommonData { + /// status information for irq chip functions. + state: IrqStatus, + /// per-IRQ data for the irq_chip methods + handler_data: Option>, + msi_desc: Option>, + // todo: affinity +} + +pub trait IrqHandlerData: Send + Sync + Any + Debug {} + +bitflags! { + /// 中断线状态 + /// https://code.dragonos.org.cn/xref/linux-6.1.9/include/linux/irq.h?fi=IRQ_TYPE_PROBE#77 + pub struct IrqLineStatus: u32 { + /// 默认,未指明类型 + const IRQ_TYPE_NONE = 0x00000000; + /// 上升沿触发 + const IRQ_TYPE_EDGE_RISING = 0x00000001; + /// 下降沿触发 + const IRQ_TYPE_EDGE_FALLING = 0x00000002; + /// 上升沿和下降沿触发 + const IRQ_TYPE_EDGE_BOTH = Self::IRQ_TYPE_EDGE_RISING.bits | Self::IRQ_TYPE_EDGE_FALLING.bits; + /// 高电平触发 + const IRQ_TYPE_LEVEL_HIGH = 0x00000004; + /// 低电平触发 + const IRQ_TYPE_LEVEL_LOW = 0x00000008; + /// 过滤掉电平位的掩码 + const IRQ_TYPE_LEVEL_MASK = Self::IRQ_TYPE_LEVEL_LOW.bits | Self::IRQ_TYPE_LEVEL_HIGH.bits; + /// 上述位掩码的掩码 + const IRQ_TYPE_SENSE_MASK = 0x0000000f; + /// 某些PICs使用此类型要求 `IrqChip::irq_set_type()` 设置硬件到一个合理的默认值 + /// (由irqdomain的map()回调使用,以便为新分配的描述符同步硬件状态和软件标志位)。 + const IRQ_TYPE_DEFAULT = Self::IRQ_TYPE_SENSE_MASK.bits; + + /// 特定于探测的过程中的特殊标志 + const IRQ_TYPE_PROBE = 0x00000010; + + /// 中断是电平类型。当上述触发位通过`IrqChip::irq_set_type()` 修改时,也会在代码中更新 + const IRQ_LEVEL = 1 << 8; + /// 标记一个PER_CPU的中断。将保护其免受亲和性设置的影响 + const IRQ_PER_CPU = 1 << 9; + /// 中断不能被自动探测 + const IRQ_NOPROBE = 1 << 10; + /// 中断不能通过request_irq()请求 + const IRQ_NOREQUEST = 1 << 11; + /// 中断在request/setup_irq()中不会自动启用 + const IRQ_NOAUTOEN = 1 << 12; + /// 中断不能被平衡(亲和性设置) + const IRQ_NO_BALANCING = 1 << 13; + /// 中断可以从进程上下文中迁移 + const IRQ_MOVE_PCNTXT = 1 << 14; + /// 中断嵌套在另一个线程中 + const IRQ_NESTED_THREAD = 1 << 15; + /// 中断不能被线程化 + const IRQ_NOTHREAD = 1 << 16; + /// Dev_id是一个per-CPU变量 + const IRQ_PER_CPU_DEVID = 1 << 17; + /// 总是由另一个中断轮询。将其从错误的中断检测机制和核心侧轮询中排除 + const IRQ_IS_POLLED = 1 << 18; + /// 禁用延迟的中断禁用 (Disable lazy irq disable) + const IRQ_DISABLE_UNLAZY = 1 << 19; + /// 在/proc/interrupts中不显示 + const IRQ_HIDDEN = 1 << 20; + /// 从note_interrupt()调试中排除 + const IRQ_NO_DEBUG = 1 << 21; + } + + + +} +bitflags! { + /// 中断状态(存储在IrqCommonData) + /// + /// 参考: https://code.dragonos.org.cn/xref/linux-6.1.9/include/linux/irq.h#227 + pub struct IrqStatus: u32 { + /// 触发类型位的掩码 + const IRQD_TRIGGER_MASK = 0xf; + /// 亲和性设置待处理 + const IRQD_SETAFFINITY_PENDING = 1 << 8; + /// 中断已激活 + const IRQD_ACTIVATED = 1 << 9; + /// 对此IRQ禁用平衡 + const IRQD_NO_BALANCING = 1 << 10; + /// 中断是每个CPU特定的 + const IRQD_PER_CPU = 1 << 11; + /// 中断亲和性已设置 + const IRQD_AFFINITY_SET = 1 << 12; + /// 中断是电平触发 + const IRQD_LEVEL = 1 << 13; + /// 中断配置为从挂起状态唤醒 + const IRQD_WAKEUP_STATE = 1 << 14; + /// 中断可以在进程上下文中移动 + const IRQD_MOVE_PCNTXT = 1 << 15; + /// 中断被禁用 + const IRQD_IRQ_DISABLED = 1 << 16; + /// 中断被屏蔽 + const IRQD_IRQ_MASKED = 1 << 17; + /// 中断正在处理中 + const IRQD_IRQ_INPROGRESS = 1 << 18; + /// 唤醒模式已准备就绪 + const IRQD_WAKEUP_ARMED = 1 << 19; + /// 中断被转发到一个虚拟CPU + const IRQD_FORWARDED_TO_VCPU = 1 << 20; + /// 亲和性由内核自动管理 + const IRQD_AFFINITY_MANAGED = 1 << 21; + /// 中断已启动 + const IRQD_IRQ_STARTED = 1 << 22; + /// 由于空亲和性掩码而关闭的中断。仅适用于亲和性管理的中断。 + const IRQD_MANAGED_SHUTDOWN = 1 << 23; + /// IRQ只允许单个亲和性目标 + const IRQD_SINGLE_TARGET = 1 << 24; + /// 预期的触发器已设置 + const IRQD_DEFAULT_TRIGGER_SET = 1 << 25; + /// 可以使用保留模式 + const IRQD_CAN_RESERVE = 1 << 26; + /// Non-maskable MSI quirk for affinity change required + const IRQD_MSI_NOMASK_QUIRK = 1 << 27; + /// 强制要求`handle_irq_()`只能在真实的中断上下文中调用 + const IRQD_HANDLE_ENFORCE_IRQCTX = 1 << 28; + /// 激活时设置亲和性。在禁用时不要调用irq_chip::irq_set_affinity()。 + const IRQD_AFFINITY_ON_ACTIVATE = 1 << 29; + /// 如果irqpm具有标志 IRQCHIP_ENABLE_WAKEUP_ON_SUSPEND,则在挂起时中断被启用。 + const IRQD_IRQ_ENABLED_ON_SUSPEND = 1 << 30; + } +} + +#[allow(dead_code)] +impl IrqStatus { + pub const fn is_set_affinity_pending(&self) -> bool { + self.contains(Self::IRQD_SETAFFINITY_PENDING) + } + + pub const fn is_per_cpu(&self) -> bool { + self.contains(Self::IRQD_PER_CPU) + } + + pub const fn can_balance(&self) -> bool { + !((self.bits & (Self::IRQD_PER_CPU.bits | Self::IRQD_NO_BALANCING.bits)) != 0) + } + + pub const fn affinity_was_set(&self) -> bool { + self.contains(Self::IRQD_AFFINITY_SET) + } + + pub fn mark_affinity_set(&mut self) { + self.insert(Self::IRQD_AFFINITY_SET); + } + + pub const fn trigger_type_was_set(&self) -> bool { + self.contains(Self::IRQD_DEFAULT_TRIGGER_SET) + } + + pub fn mark_trigger_type_set(&mut self) { + self.insert(Self::IRQD_DEFAULT_TRIGGER_SET); + } + + pub const fn trigger_type(&self) -> IrqLineStatus { + IrqLineStatus::from_bits_truncate(self.bits & Self::IRQD_TRIGGER_MASK.bits) + } + + /// Must only be called inside irq_chip.irq_set_type() functions or + /// from the DT/ACPI setup code. + pub const fn set_trigger_type(&mut self, trigger: IrqLineStatus) { + self.bits &= !Self::IRQD_TRIGGER_MASK.bits; + self.bits |= trigger.bits & Self::IRQD_TRIGGER_MASK.bits; + + self.bits |= Self::IRQD_DEFAULT_TRIGGER_SET.bits; + } + + pub const fn is_level_type(&self) -> bool { + self.contains(Self::IRQD_LEVEL) + } + + /// Must only be called of irqchip.irq_set_affinity() or low level + /// hierarchy domain allocation functions. + pub fn set_single_target(&mut self) { + self.insert(Self::IRQD_SINGLE_TARGET); + } + + pub const fn is_single_target(&self) -> bool { + self.contains(Self::IRQD_SINGLE_TARGET) + } + + pub fn set_handle_enforce_irqctx(&mut self) { + self.insert(Self::IRQD_HANDLE_ENFORCE_IRQCTX); + } + + pub const fn is_handle_enforce_irqctx(&self) -> bool { + self.contains(Self::IRQD_HANDLE_ENFORCE_IRQCTX) + } + + pub const fn is_enabled_on_suspend(&self) -> bool { + self.contains(Self::IRQD_IRQ_ENABLED_ON_SUSPEND) + } + + pub const fn is_wakeup_set(&self) -> bool { + self.contains(Self::IRQD_WAKEUP_STATE) + } + + pub const fn can_move_in_process_context(&self) -> bool { + self.contains(Self::IRQD_MOVE_PCNTXT) + } + + pub const fn is_irq_disabled(&self) -> bool { + self.contains(Self::IRQD_IRQ_DISABLED) + } + + pub const fn is_irq_masked(&self) -> bool { + self.contains(Self::IRQD_IRQ_MASKED) + } + + pub const fn is_irq_in_progress(&self) -> bool { + self.contains(Self::IRQD_IRQ_INPROGRESS) + } + + pub const fn is_wakeup_armed(&self) -> bool { + self.contains(Self::IRQD_WAKEUP_ARMED) + } + + pub const fn is_forwarded_to_vcpu(&self) -> bool { + self.contains(Self::IRQD_FORWARDED_TO_VCPU) + } + + pub fn set_forwarded_to_vcpu(&mut self) { + self.insert(Self::IRQD_FORWARDED_TO_VCPU); + } + + pub const fn is_affinity_managed(&self) -> bool { + self.contains(Self::IRQD_AFFINITY_MANAGED) + } + + pub const fn is_activated(&self) -> bool { + self.contains(Self::IRQD_ACTIVATED) + } + + pub fn set_activated(&mut self) { + self.insert(Self::IRQD_ACTIVATED); + } + + pub fn clear_activated(&mut self) { + self.remove(Self::IRQD_ACTIVATED); + } + + pub const fn is_started(&self) -> bool { + self.contains(Self::IRQD_IRQ_STARTED) + } + + pub const fn is_managed_and_shutdown(&self) -> bool { + self.contains(Self::IRQD_MANAGED_SHUTDOWN) + } + + pub fn set_can_reserve(&mut self) { + self.insert(Self::IRQD_CAN_RESERVE); + } + + pub const fn can_reserve(&self) -> bool { + self.contains(Self::IRQD_CAN_RESERVE) + } + + pub fn clear_can_reserve(&mut self) { + self.remove(Self::IRQD_CAN_RESERVE); + } + + pub fn set_msi_nomask_quirk(&mut self) { + self.insert(Self::IRQD_MSI_NOMASK_QUIRK); + } + + pub fn clear_msi_nomask_quirk(&mut self) { + self.remove(Self::IRQD_MSI_NOMASK_QUIRK); + } + + pub const fn is_msi_nomask_quirk(&self) -> bool { + self.contains(Self::IRQD_MSI_NOMASK_QUIRK) + } + + pub fn set_affinity_on_activate(&mut self) { + self.insert(Self::IRQD_AFFINITY_ON_ACTIVATE); + } + + pub const fn is_affinity_on_activate(&self) -> bool { + self.contains(Self::IRQD_AFFINITY_ON_ACTIVATE) + } +} diff --git a/kernel/src/exception/irqdomain.rs b/kernel/src/exception/irqdomain.rs new file mode 100644 index 00000000..f6327ee7 --- /dev/null +++ b/kernel/src/exception/irqdomain.rs @@ -0,0 +1,184 @@ +use core::fmt::Debug; + +use alloc::{ + string::String, + sync::{Arc, Weak}, + vec::Vec, +}; +use hashbrown::HashMap; +use system_error::SystemError; + +use crate::{ + driver::{base::device::Device, open_firmware::device_node::DeviceNode}, + libs::{rwlock::RwLock, spinlock::SpinLock}, +}; + +use super::{ + irqchip::{IrqChipGeneric, IrqGcFlags}, + HardwareIrqNumber, IrqNumber, +}; + +/// 中断域 +/// +/// 用于把硬件中断号翻译为软件中断号的映射的对象 +/// +/// 参考 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: Option, + /// 中断域的操作 + ops: &'static dyn IrqDomainOps, + inner: SpinLock, + /// 中断号反向映射 + revmap: RwLock, +} + +#[allow(dead_code)] +#[derive(Debug)] +struct InnerIrqDomain { + /// 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>, + /// Pointer to a device that the domain represent, and that will be + /// used for power management purposes. + device: Option>, + /// Pointer to parent irq_domain to support hierarchy irq_domains + parent: Option>, +} + +impl IrqDomain { + #[allow(dead_code)] + pub fn new( + name: Option<&'static str>, + allocated_name: Option, + ops: &'static dyn IrqDomainOps, + flags: IrqDomainFlags, + bus_token: IrqDomainBusToken, + ) -> Option> { + if name.is_none() && allocated_name.is_none() { + return None; + } + + let x = IrqDomain { + name, + allocated_name, + ops, + inner: SpinLock::new(InnerIrqDomain { + flags, + mapcount: 0, + bus_token, + generic_chip: None, + device: None, + parent: None, + }), + revmap: RwLock::new(IrqDomainRevMap { + map: HashMap::new(), + hwirq_max: HardwareIrqNumber::new(0), + }), + }; + + return Some(Arc::new(x)); + } +} + +/// 参考 https://code.dragonos.org.cn/xref/linux-6.1.9/include/linux/irqdomain.h#190 +#[allow(dead_code)] +#[derive(Debug)] +struct IrqDomainRevMap { + map: HashMap, + hwirq_max: HardwareIrqNumber, +} + +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 { + /// 匹配一个中断控制器设备节点到一个主机。 + fn match_node( + &self, + irq_domain: &Arc, + device_node: &Arc, + bus_token: IrqDomainBusToken, + ) -> bool; + + /// 创建或更新一个虚拟中断号与一个硬件中断号之间的映射。 + /// 对于给定的映射,这只会被调用一次。 + fn map( + &self, + irq_domain: &Arc, + hwirq: HardwareIrqNumber, + virq: IrqNumber, + ) -> Result<(), SystemError>; + + /// 删除一个虚拟中断号与一个硬件中断号之间的映射。 + fn unmap(&self, irq_domain: &Arc, virq: IrqNumber); +} + +#[allow(dead_code)] +#[derive(Debug)] +pub struct IrqDomainChipGeneric { + inner: SpinLock, +} + +#[allow(dead_code)] +#[derive(Debug)] +struct InnerIrqDomainChipGeneric { + irqs_per_chip: u32, + flags_to_clear: IrqGcFlags, + flags_to_set: IrqGcFlags, + gc_flags: IrqGcFlags, + gc: Vec>, +} diff --git a/kernel/src/exception/mod.rs b/kernel/src/exception/mod.rs index 78259c37..d199c117 100644 --- a/kernel/src/exception/mod.rs +++ b/kernel/src/exception/mod.rs @@ -4,6 +4,10 @@ use crate::arch::CurrentIrqArch; pub mod init; pub mod ipi; +pub mod irqchip; +pub mod irqdata; +pub mod irqdomain; +pub mod msi; pub mod softirq; /// 中断的架构相关的trait @@ -77,3 +81,11 @@ impl Drop for IrqFlagsGuard { } } } + +// 定义中断号结构体 +// 用于表示软件逻辑视角的中断号,全局唯一 +int_like!(IrqNumber, u32); + +// 硬件中断号 +// 用于表示在某个IrqDomain中的中断号 +int_like!(HardwareIrqNumber, u32); diff --git a/kernel/src/exception/msi.rs b/kernel/src/exception/msi.rs new file mode 100644 index 00000000..31fcb1e7 --- /dev/null +++ b/kernel/src/exception/msi.rs @@ -0,0 +1,126 @@ +use core::{any::Any, fmt::Debug}; + +use alloc::sync::Arc; + +use crate::{ + driver::{base::device::Device, pci::pci_irq::PciIrqMsg}, + filesystem::sysfs::Attribute, + libs::spinlock::SpinLock, +}; + +use super::IrqNumber; + +#[derive(Clone, Copy)] +pub struct MsiMsg { + pub address_lo: u32, + pub address_hi: u32, + pub data: u32, +} + +impl Debug for MsiMsg { + fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result { + write!( + f, + "MsiMsg {{ address_lo: 0x{:x}, address_hi: 0x{:x}, data: 0x{:x} }}", + self.address_lo, self.address_hi, self.data + ) + } +} + +#[allow(dead_code)] +impl MsiMsg { + /// Create a new MSI message + pub const fn new(address: u64, data: u32) -> Self { + MsiMsg { + address_lo: address as u32, + address_hi: (address >> 32) as u32, + data, + } + } + + /// Create a new MSI message + pub const fn new_lo_hi(address_lo: u32, address_hi: u32, data: u32) -> Self { + MsiMsg { + address_lo, + address_hi, + data, + } + } + + /// Get the address of the MSI message + pub const fn address(&self) -> u64 { + (self.address_hi as u64) << 32 | self.address_lo as u64 + } +} + +#[allow(dead_code)] +#[derive(Debug)] +pub struct MsiDesc { + inner: SpinLock, +} + +#[allow(dead_code)] +#[derive(Debug)] +struct InnerMsiDesc { + /// The base interrupt number + irq: IrqNumber, + /// The number of vectors used + nvec_used: u32, + /// Pointer to the device which uses this descriptor + dev: Option>, + /// The last set MSI message cached for reuse + msg: MsiMsg, + /// Pointer to sysfs device attribute + sysfs_attribute: Option<&'static dyn Attribute>, + /// Pointer to MSI callback function + func: Option<&'static dyn MsiDescFunc>, + /// The index of the MSI descriptor + index: u32, + /// PCI specific msi descriptor data + pci_msg: PciIrqMsg, +} + +#[allow(dead_code)] +impl MsiDesc { + pub const fn new( + irq: IrqNumber, + nvec_used: u32, + dev: Option>, + index: u32, + pci_msg: PciIrqMsg, + ) -> Self { + MsiDesc { + inner: SpinLock::new(InnerMsiDesc { + irq, + nvec_used, + dev, + msg: MsiMsg { + address_lo: 0, + address_hi: 0, + data: 0, + }, + sysfs_attribute: None, + func: None, + index, + pci_msg, + }), + } + } + + pub fn set_msg(&self, msg: MsiMsg) { + self.inner.lock().msg = msg; + } + + pub fn msg(&self) -> MsiMsg { + self.inner.lock().msg + } +} + +pub trait MsiDescFunc: Debug { + /// Callback that may be called when the MSI message + /// address or data changes. + fn write_msi_msg(&self, data: Arc); +} + +/// Data parameter for the `MsiDescFunc` callback. +pub trait MsiDescFuncData: Send + Sync + Any {}