添加irqchip这一层的数据结构(尚未接入真实的芯片) (#520)

* 添加irqchip这一层的数据结构(尚未接入真实的芯片)
This commit is contained in:
LoGin 2024-02-18 20:41:41 +08:00 committed by GitHub
parent ca318c376b
commit ce5850adbf
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
10 changed files with 1079 additions and 8 deletions

View File

@ -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",

View File

@ -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<String>,
inner: SpinLock<InnerDeviceNode>,
}
#[allow(dead_code)]
#[derive(Debug)]
struct InnerDeviceNode {
properties: Vec<Property>,
parent: Weak<DeviceNode>,
children: Vec<Arc<DeviceNode>>,
sibling: Option<Weak<DeviceNode>>,
private_data: Option<Arc<dyn DeviceNodePrivateData>>,
}
#[allow(dead_code)]
impl DeviceNode {
pub fn new(
full_name: Option<&'static str>,
full_name_allocated: Option<String>,
) -> Option<Arc<Self>> {
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<Property> {
self.inner.lock().properties.clone()
}
pub fn parent(&self) -> Option<Arc<DeviceNode>> {
self.inner.lock().parent.upgrade()
}
pub fn set_parent(&self, parent: Arc<DeviceNode>) {
self.inner.lock().parent = Arc::downgrade(&parent);
}
pub fn children(&self) -> Vec<Arc<DeviceNode>> {
self.inner.lock().children.clone()
}
pub fn add_child(&self, child: Arc<DeviceNode>) {
self.inner.lock().children.push(child);
}
pub fn sibling(&self) -> Option<Arc<DeviceNode>> {
self.inner.lock().sibling.as_ref().and_then(|s| s.upgrade())
}
pub fn set_sibling(&self, sibling: Arc<DeviceNode>) {
self.inner.lock().sibling = Some(Arc::downgrade(&sibling));
}
pub fn private_data(&self) -> Option<Arc<dyn DeviceNodePrivateData>> {
self.inner.lock().private_data.clone()
}
pub fn set_private_data(&self, data: Arc<dyn DeviceNodePrivateData>) {
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<Arc<KernFSInode>>) {
todo!()
}
fn inode(&self) -> Option<Arc<KernFSInode>> {
todo!()
}
fn parent(&self) -> Option<Weak<dyn KObject>> {
todo!()
}
fn set_parent(&self, _parent: Option<Weak<dyn KObject>>) {
todo!()
}
fn kset(&self) -> Option<Arc<KSet>> {
todo!()
}
fn set_kset(&self, _kset: Option<Arc<KSet>>) {
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<KObjectState> {
todo!()
}
fn kobj_state_mut(&self) -> RwLockWriteGuard<KObjectState> {
todo!()
}
fn set_kobj_state(&self, _state: KObjectState) {
todo!()
}
}
#[allow(dead_code)]
#[derive(Debug, Clone)]
pub struct Property {
name: String,
value: Vec<u8>,
bin_attr: Option<Arc<dyn BinAttribute>>,
}
impl Property {
#[allow(dead_code)]
pub const fn new(name: String, value: Vec<u8>, battr: Option<Arc<dyn BinAttribute>>) -> Self {
Property {
name,
value,
bin_attr: battr,
}
}
}

View File

@ -1,2 +1,3 @@
// #[cfg(target_arch = "riscv64")]
pub mod device_node;
pub mod fdt;

View File

@ -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<u8, PciError> {
fn irq_install(&mut self, msg: PciIrqMsg) -> Result<u8, PciError> {
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<u8, PciError> {
fn msi_install(&mut self, msg: PciIrqMsg) -> Result<u8, PciError> {
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<u8, PciError> {
fn msix_install(&mut self, msg: PciIrqMsg) -> Result<u8, PciError> {
if let Some(irq_type) = self.irq_type_mut() {
match *irq_type {
IrqType::Msix {

View File

@ -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",

View File

@ -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<IrqData>) -> Result<(), SystemError> {
Err(SystemError::ENOSYS)
}
/// shut down the interrupt (defaults to ->disable if ENOSYS)
fn irq_shutdown(&self, _irq: &Arc<IrqData>) -> Result<(), SystemError> {
Err(SystemError::ENOSYS)
}
/// enable the interrupt
///
/// (defaults to ->unmask if ENOSYS)
fn irq_enable(&self, _irq: &Arc<IrqData>) -> Result<(), SystemError> {
Err(SystemError::ENOSYS)
}
/// disable the interrupt
fn irq_disable(&self, irq: &Arc<IrqData>);
/// start of a new interrupt
fn irq_ack(&self, irq: &Arc<IrqData>);
/// mask an interrupt source
fn irq_mask(&self, irq: &Arc<IrqData>);
/// ack and mask an interrupt source
fn irq_mask_ack(&self, irq: &Arc<IrqData>);
/// unmask an interrupt source
fn irq_unmask(&self, irq: &Arc<IrqData>);
/// end of interrupt
fn irq_eoi(&self, irq: &Arc<IrqData>);
// todo: set affinity
/// retrigger an IRQ to the CPU
fn retrigger(&self, irq: &Arc<IrqData>);
/// set the flow type of an interrupt
///
/// flow_type: the flow type to set
///
fn irq_set_type(
&self,
_irq: &Arc<IrqData>,
_flow_type: IrqLineStatus,
) -> Result<(), SystemError> {
Err(SystemError::ENOSYS)
}
/// enable/disable power management wake-on of an interrupt
fn irq_set_wake(&self, _irq: &Arc<IrqData>, _on: bool) -> Result<(), SystemError> {
Err(SystemError::ENOSYS)
}
/// function to lock access to slow bus (i2c) chips
fn irq_bus_lock(&self, _irq: &Arc<IrqData>) -> Result<(), SystemError> {
Ok(())
}
/// function to sync and unlock slow bus (i2c) chips
fn irq_bus_sync_unlock(&self, _irq: &Arc<IrqData>) -> 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<IrqData>);
/// function called from core code on resume once per chip,
/// when one ore more interrupts are installed
fn irq_resume(&self, irq: &Arc<IrqData>);
/// function called from core code on shutdown once per chip
fn irq_pm_shutdown(&self, irq: &Arc<IrqData>);
/// Optional function to set irq_data.mask for special cases
fn irq_calc_mask(&self, _irq: &Arc<IrqData>) {}
// todo: print chip
/// optional to request resources before calling
/// any other callback related to this irq
fn irq_request_resources(&self, _irq: &Arc<IrqData>) -> Result<(), SystemError> {
Ok(())
}
/// optional to release resources acquired with
/// irq_request_resources
fn irq_release_resources(&self, _irq: &Arc<IrqData>) {}
/// optional to compose message content for MSI
fn irq_compose_msi_msg(&self, _irq: &Arc<IrqData>, _msg: &mut MsiMsg) {}
/// optional to write message content for MSI
fn irq_write_msi_msg(&self, _irq: &Arc<IrqData>, _msg: &MsiMsg) {}
/// return the internal state of an interrupt
fn irqchip_state(
&self,
_irq: &Arc<IrqData>,
_which: IrqChipState,
) -> Result<bool, SystemError> {
Err(SystemError::ENOSYS)
}
/// set the internal state of an interrupt
fn set_irqchip_state(
&self,
_irq: &Arc<IrqData>,
_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<IrqData>, cpu: u32);
// todo: send ipi with cpu mask
/// function called from core code before enabling an NMI
fn irq_nmi_setup(&self, _irq: &Arc<IrqData>) -> Result<(), SystemError> {
Err(SystemError::ENOSYS)
}
/// function called from core code after disabling an NMI
fn irq_nmi_teardown(&self, irq: &Arc<IrqData>);
}
#[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<InnerIrqChipGeneric>,
}
#[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<Arc<dyn IrqChipGenericPrivateData>>,
installed: u64,
unused: u64,
domain: Weak<IrqDomain>,
chip_types: Vec<IrqChipType>,
}
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<IrqChipGeneric>);
/// 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<IrqChipGeneric>);
}
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
}

View File

@ -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<IrqCommonData>,
/// 绑定到的中断芯片
chip: Arc<dyn IrqChip>,
/// 中断芯片的私有数据与当前irq相关
chip_data: Arc<dyn IrqChipData>,
/// 中断域
domain: Arc<IrqDomain>,
/// 中断的父中断(如果具有中断域继承的话)
parent_data: Option<Weak<IrqData>>,
}
/// 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<InnerIrqCommonData>,
}
#[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<Arc<dyn IrqHandlerData>>,
msi_desc: Option<Arc<MsiDesc>>,
// 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)
}
}

View File

@ -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<String>,
/// 中断域的操作
ops: &'static dyn IrqDomainOps,
inner: SpinLock<InnerIrqDomain>,
/// 中断号反向映射
revmap: RwLock<IrqDomainRevMap>,
}
#[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<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,
) -> Option<Arc<Self>> {
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<HardwareIrqNumber, IrqNumber>,
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<IrqDomain>,
device_node: &Arc<DeviceNode>,
bus_token: IrqDomainBusToken,
) -> bool;
/// 创建或更新一个虚拟中断号与一个硬件中断号之间的映射。
/// 对于给定的映射,这只会被调用一次。
fn map(
&self,
irq_domain: &Arc<IrqDomain>,
hwirq: HardwareIrqNumber,
virq: IrqNumber,
) -> Result<(), SystemError>;
/// 删除一个虚拟中断号与一个硬件中断号之间的映射。
fn unmap(&self, irq_domain: &Arc<IrqDomain>, virq: IrqNumber);
}
#[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>>,
}

View File

@ -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);

126
kernel/src/exception/msi.rs Normal file
View File

@ -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<InnerMsiDesc>,
}
#[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<Arc<dyn Device>>,
/// 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<Arc<dyn Device>>,
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<dyn MsiDescFuncData>);
}
/// Data parameter for the `MsiDescFunc` callback.
pub trait MsiDescFuncData: Send + Sync + Any {}