mirror of
https://github.com/DragonOS-Community/DragonOS.git
synced 2025-06-08 18:26:48 +00:00
feat:(riscv/intr) 实现riscv plic驱动,能处理外部中断 (#799)
* feat:(riscv/intr) 实现riscv plic驱动,能处理外部中断 - 实现riscv plic驱动,能处理外部中断 - 能收到virtio-blk的中断 - 实现fasteoi interrupt handler
This commit is contained in:
parent
17dc558977
commit
0102d69fdd
@ -47,8 +47,8 @@ num-traits = { git = "https://git.mirrors.dragonos.org.cn/DragonOS-Community/num
|
||||
smoltcp = { version = "=0.11.0", default-features = false, features = ["log", "alloc", "socket-raw", "socket-udp", "socket-tcp", "socket-icmp", "socket-dhcpv4", "socket-dns", "proto-ipv4", "proto-ipv6"]}
|
||||
system_error = { path = "crates/system_error" }
|
||||
unified-init = { path = "crates/unified-init" }
|
||||
virtio-drivers = { git = "https://git.mirrors.dragonos.org.cn/DragonOS-Community/virtio-drivers", rev = "448a781" }
|
||||
fdt = "=0.1.5"
|
||||
virtio-drivers = { git = "https://git.mirrors.dragonos.org.cn/DragonOS-Community/virtio-drivers", rev = "f91c807965" }
|
||||
fdt = { git = "https://git.mirrors.dragonos.org.cn/DragonOS-Community/fdt", rev = "9862813020" }
|
||||
uefi = { version = "=0.26.0", features = ["alloc"] }
|
||||
uefi-raw = "=0.5.0"
|
||||
paste = "=1.0.14"
|
||||
|
@ -20,6 +20,12 @@ impl AllocBitmap {
|
||||
core: BitMapCore::new(),
|
||||
}
|
||||
}
|
||||
|
||||
pub fn bitand_assign(&mut self, rhs: &Self) {
|
||||
for i in 0..rhs.data.len() {
|
||||
self.data[i] &= rhs.data[i];
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl BitMapOps<usize> for AllocBitmap {
|
||||
@ -111,8 +117,8 @@ impl BitMapOps<usize> for AllocBitmap {
|
||||
}
|
||||
}
|
||||
|
||||
impl BitAnd for AllocBitmap {
|
||||
type Output = Self;
|
||||
impl BitAnd for &AllocBitmap {
|
||||
type Output = AllocBitmap;
|
||||
|
||||
fn bitand(self, rhs: Self) -> Self::Output {
|
||||
let mut result = AllocBitmap::new(self.elements);
|
||||
@ -122,3 +128,11 @@ impl BitAnd for AllocBitmap {
|
||||
result
|
||||
}
|
||||
}
|
||||
|
||||
impl BitAnd for AllocBitmap {
|
||||
type Output = AllocBitmap;
|
||||
|
||||
fn bitand(self, rhs: Self) -> Self::Output {
|
||||
&self & &rhs
|
||||
}
|
||||
}
|
||||
|
@ -663,3 +663,23 @@ fn test_alloc_bitmap_bitand_128() {
|
||||
assert_eq!(bitmap3.first_false_index(), Some(2));
|
||||
assert_eq!(bitmap3.last_index(), Some(67));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_alloc_bitmap_bitand_assign_128() {
|
||||
let mut bitmap = AllocBitmap::new(128);
|
||||
bitmap.set_all(true);
|
||||
|
||||
let mut bitmap2 = AllocBitmap::new(128);
|
||||
|
||||
bitmap2.set(0, true);
|
||||
bitmap2.set(1, true);
|
||||
bitmap2.set(67, true);
|
||||
|
||||
bitmap.bitand_assign(&bitmap2);
|
||||
|
||||
assert_eq!(bitmap.len(), 128);
|
||||
assert_eq!(bitmap.size(), 16);
|
||||
assert_eq!(bitmap.first_index(), Some(0));
|
||||
assert_eq!(bitmap.first_false_index(), Some(2));
|
||||
assert_eq!(bitmap.last_index(), Some(67));
|
||||
}
|
||||
|
@ -4,6 +4,7 @@
|
||||
/// @param x 目标u64
|
||||
/// @return i32 bit-number(0..63) of the first (least significant) zero bit.
|
||||
#[inline]
|
||||
#[allow(dead_code)]
|
||||
pub fn ffz(x: u64) -> i32 {
|
||||
(!x).trailing_zeros() as i32
|
||||
}
|
||||
|
@ -3,7 +3,6 @@ use system_error::SystemError;
|
||||
use crate::{
|
||||
driver::open_firmware::fdt::OpenFirmwareFdtDriver,
|
||||
init::boot_params,
|
||||
kdebug,
|
||||
libs::align::page_align_up,
|
||||
mm::{mmio_buddy::mmio_pool, MemoryManagementArch, PhysAddr},
|
||||
};
|
||||
|
@ -1,7 +1,5 @@
|
||||
use crate::arch::{
|
||||
asm::csr::{
|
||||
CSR_SCAUSE, CSR_SEPC, CSR_SSCRATCH, CSR_SSTATUS, CSR_STVAL, SR_FS_VS, SR_SPP, SR_SUM,
|
||||
},
|
||||
asm::csr::{CSR_SCAUSE, CSR_SEPC, CSR_SSCRATCH, CSR_SSTATUS, CSR_STVAL, SR_SPP},
|
||||
cpu::LocalContext,
|
||||
interrupt::TrapFrame,
|
||||
};
|
||||
|
@ -5,9 +5,7 @@ use core::hint::spin_loop;
|
||||
|
||||
use system_error::SystemError;
|
||||
|
||||
use crate::{
|
||||
arch::syscall::syscall_handler, driver::irqchip::riscv_intc::riscv_intc_irq, kdebug, kerror,
|
||||
};
|
||||
use crate::{arch::syscall::syscall_handler, driver::irqchip::riscv_intc::riscv_intc_irq, kerror};
|
||||
|
||||
use super::TrapFrame;
|
||||
|
||||
|
@ -2,7 +2,7 @@ use riscv::register::{scause::Scause, sstatus::Sstatus};
|
||||
use system_error::SystemError;
|
||||
|
||||
use crate::{
|
||||
driver::irqchip::riscv_intc::riscv_intc_init,
|
||||
driver::irqchip::{riscv_intc::riscv_intc_init, riscv_sifive_plic::riscv_sifive_plic_init},
|
||||
exception::{InterruptArch, IrqFlags, IrqFlagsGuard, IrqNumber},
|
||||
libs::align::align_up,
|
||||
};
|
||||
@ -17,6 +17,9 @@ pub struct RiscV64InterruptArch;
|
||||
|
||||
impl InterruptArch for RiscV64InterruptArch {
|
||||
unsafe fn arch_irq_init() -> Result<(), SystemError> {
|
||||
Self::interrupt_disable();
|
||||
riscv_sifive_plic_init()?;
|
||||
// 注意,intc的初始化必须在plic之后,不然会导致plic无法关联上中断
|
||||
riscv_intc_init()?;
|
||||
|
||||
Ok(())
|
||||
|
@ -43,6 +43,7 @@ pub struct RiscV64MMArch;
|
||||
|
||||
impl RiscV64MMArch {
|
||||
/// 使远程cpu的TLB中,指定地址范围的页失效
|
||||
#[allow(dead_code)]
|
||||
pub fn remote_invalidate_page(
|
||||
cpu: ProcessorId,
|
||||
address: VirtAddr,
|
||||
@ -57,6 +58,7 @@ impl RiscV64MMArch {
|
||||
}
|
||||
|
||||
/// 使指定远程cpu的TLB中,所有范围的页失效
|
||||
#[allow(dead_code)]
|
||||
pub fn remote_invalidate_all(cpu: ProcessorId) -> Result<(), SbiRet> {
|
||||
let r = Self::remote_invalidate_page(
|
||||
cpu,
|
||||
|
@ -227,16 +227,23 @@ pub fn x86_vector_domain() -> &'static Arc<IrqDomain> {
|
||||
|
||||
#[inline(never)]
|
||||
pub fn arch_early_irq_init() -> Result<(), SystemError> {
|
||||
const IRQ_SIZE: u32 = 223;
|
||||
let vec_domain = irq_domain_manager()
|
||||
.create_and_add(
|
||||
"VECTOR".to_string(),
|
||||
&X86VectorDomainOps,
|
||||
IrqNumber::new(32),
|
||||
HardwareIrqNumber::new(32),
|
||||
223,
|
||||
IRQ_SIZE,
|
||||
)
|
||||
.ok_or(SystemError::ENOMEM)?;
|
||||
irq_domain_manager().set_default_domain(vec_domain.clone());
|
||||
irq_domain_manager().domain_associate_many(
|
||||
&vec_domain,
|
||||
IrqNumber::new(0),
|
||||
HardwareIrqNumber::new(0),
|
||||
IRQ_SIZE,
|
||||
);
|
||||
unsafe { X86_VECTOR_DOMAIN = Some(vec_domain) };
|
||||
|
||||
let apic_chip = Arc::new(LocalApicChip::new());
|
||||
|
@ -32,6 +32,7 @@ use crate::{
|
||||
VirtIODevice, VirtIODeviceIndex, VirtIODriver, VIRTIO_VENDOR_ID,
|
||||
},
|
||||
},
|
||||
exception::{irqdesc::IrqReturn, IrqNumber},
|
||||
filesystem::{kernfs::KernFSInode, mbr::MbrDiskPartionTable},
|
||||
init::initcall::INITCALL_POSTCORE,
|
||||
libs::{
|
||||
@ -85,15 +86,15 @@ unsafe impl Sync for VirtIOBlkDevice {}
|
||||
|
||||
impl VirtIOBlkDevice {
|
||||
pub fn new(transport: VirtIOTransport, dev_id: Arc<DeviceId>) -> Option<Arc<Self>> {
|
||||
let irq = transport.irq().map(|irq| IrqNumber::new(irq.data()));
|
||||
let device_inner = VirtIOBlk::<HalImpl, VirtIOTransport>::new(transport);
|
||||
if let Err(e) = device_inner {
|
||||
kerror!("VirtIOBlkDevice '{dev_id:?}' create failed: {:?}", e);
|
||||
return None;
|
||||
}
|
||||
// !!!! 在这里临时测试virtio-blk的读写功能,后续需要删除 !!!!
|
||||
// 目前read会报错 `NotReady`
|
||||
let device_inner: VirtIOBlk<HalImpl, VirtIOTransport> = device_inner.unwrap();
|
||||
|
||||
let mut device_inner: VirtIOBlk<HalImpl, VirtIOTransport> = device_inner.unwrap();
|
||||
device_inner.enable_interrupts();
|
||||
let dev = Arc::new_cyclic(|self_ref| Self {
|
||||
self_ref: self_ref.clone(),
|
||||
dev_id,
|
||||
@ -104,6 +105,7 @@ impl VirtIOBlkDevice {
|
||||
virtio_index: None,
|
||||
device_common: DeviceCommonData::default(),
|
||||
kobject_common: KObjectCommonData::default(),
|
||||
irq,
|
||||
}),
|
||||
});
|
||||
|
||||
@ -190,6 +192,7 @@ struct InnerVirtIOBlkDevice {
|
||||
virtio_index: Option<VirtIODeviceIndex>,
|
||||
device_common: DeviceCommonData,
|
||||
kobject_common: KObjectCommonData,
|
||||
irq: Option<IrqNumber>,
|
||||
}
|
||||
|
||||
impl Debug for InnerVirtIOBlkDevice {
|
||||
@ -199,11 +202,16 @@ impl Debug for InnerVirtIOBlkDevice {
|
||||
}
|
||||
|
||||
impl VirtIODevice for VirtIOBlkDevice {
|
||||
fn irq(&self) -> Option<IrqNumber> {
|
||||
self.inner().irq
|
||||
}
|
||||
|
||||
fn handle_irq(
|
||||
&self,
|
||||
_irq: crate::exception::IrqNumber,
|
||||
) -> Result<crate::exception::irqdesc::IrqReturn, system_error::SystemError> {
|
||||
todo!("VirtIOBlkDevice::handle_irq")
|
||||
) -> Result<IrqReturn, system_error::SystemError> {
|
||||
// todo: handle virtio blk irq
|
||||
Ok(crate::exception::irqdesc::IrqReturn::Handled)
|
||||
}
|
||||
|
||||
fn dev_id(&self) -> &Arc<DeviceId> {
|
||||
|
@ -6,14 +6,17 @@ use system_error::SystemError;
|
||||
|
||||
use crate::{
|
||||
arch::{interrupt::TrapFrame, time::riscv_time_base_freq, CurrentIrqArch, CurrentTimeArch},
|
||||
driver::base::device::DeviceId,
|
||||
driver::{
|
||||
base::device::DeviceId,
|
||||
irqchip::riscv_intc::{riscv_intc_assicate_irq, riscv_intc_hwirq_to_virq},
|
||||
},
|
||||
exception::{
|
||||
irqdata::{IrqHandlerData, IrqLineStatus},
|
||||
irqdesc::{
|
||||
irq_desc_manager, IrqDesc, IrqFlowHandler, IrqHandleFlags, IrqHandler, IrqReturn,
|
||||
},
|
||||
manage::irq_manager,
|
||||
InterruptArch, IrqNumber,
|
||||
HardwareIrqNumber, InterruptArch, IrqNumber,
|
||||
},
|
||||
libs::spinlock::SpinLock,
|
||||
mm::percpu::PerCpu,
|
||||
@ -42,7 +45,7 @@ static mut HART0_NSEC_PASSED: usize = 0;
|
||||
static mut HART0_LAST_UPDATED: u64 = 0;
|
||||
|
||||
impl RiscVSbiTimer {
|
||||
pub const TIMER_IRQ: IrqNumber = IrqNumber::from(5);
|
||||
pub const TIMER_IRQ: HardwareIrqNumber = HardwareIrqNumber::new(5);
|
||||
|
||||
fn handle_irq(trap_frame: &mut TrapFrame) -> Result<(), SystemError> {
|
||||
// 更新下一次中断时间
|
||||
@ -117,7 +120,7 @@ pub fn riscv_sbi_timer_init_local() {
|
||||
|
||||
irq_manager()
|
||||
.request_irq(
|
||||
RiscVSbiTimer::TIMER_IRQ,
|
||||
riscv_intc_hwirq_to_virq(RiscVSbiTimer::TIMER_IRQ).unwrap(),
|
||||
"riscv_clocksource".to_string(),
|
||||
&RiscvSbiTimerHandler,
|
||||
IrqHandleFlags::IRQF_SHARED | IrqHandleFlags::IRQF_PERCPU,
|
||||
@ -136,7 +139,8 @@ pub fn riscv_sbi_timer_init_local() {
|
||||
|
||||
#[inline(never)]
|
||||
pub fn riscv_sbi_timer_irq_desc_init() {
|
||||
let desc = irq_desc_manager().lookup(RiscVSbiTimer::TIMER_IRQ).unwrap();
|
||||
let virq = riscv_intc_assicate_irq(RiscVSbiTimer::TIMER_IRQ).unwrap();
|
||||
let desc = irq_desc_manager().lookup(virq).unwrap();
|
||||
|
||||
desc.modify_status(IrqLineStatus::IRQ_LEVEL, IrqLineStatus::empty());
|
||||
desc.set_handler(&RiscvSbiTimerIrqFlowHandler);
|
||||
|
@ -1,2 +1,4 @@
|
||||
#[cfg(target_arch = "riscv64")]
|
||||
pub mod riscv_intc;
|
||||
#[cfg(target_arch = "riscv64")]
|
||||
pub mod riscv_sifive_plic;
|
||||
|
@ -17,6 +17,8 @@ use crate::{
|
||||
sched::{SchedMode, __schedule},
|
||||
};
|
||||
|
||||
use super::riscv_sifive_plic::do_plic_irq;
|
||||
|
||||
static mut RISCV_INTC_DOMAIN: Option<Arc<IrqDomain>> = None;
|
||||
static mut RISCV_INTC_CHIP: Option<Arc<RiscvIntcChip>> = None;
|
||||
|
||||
@ -30,6 +32,9 @@ fn riscv_intc_chip() -> Option<&'static Arc<RiscvIntcChip>> {
|
||||
unsafe { RISCV_INTC_CHIP.as_ref() }
|
||||
}
|
||||
|
||||
/// RISC-V INTC虚拟中断号的起始值(192映射物理的0)
|
||||
pub const RISCV_INTC_VIRQ_START: u32 = 192;
|
||||
|
||||
#[derive(Debug)]
|
||||
struct RiscvIntcChip {
|
||||
inner: SpinLock<InnerIrqChip>,
|
||||
@ -87,6 +92,7 @@ impl IrqChip for RiscvIntcChip {
|
||||
}
|
||||
|
||||
impl RiscvIntcChip {
|
||||
const IRQ_SIZE: u32 = 64;
|
||||
fn new() -> Self {
|
||||
Self {
|
||||
inner: SpinLock::new(InnerIrqChip {
|
||||
@ -143,7 +149,11 @@ pub unsafe fn riscv_intc_init() -> Result<(), SystemError> {
|
||||
}
|
||||
|
||||
let intc_domain = irq_domain_manager()
|
||||
.create_and_add_linear("riscv-intc".to_string(), &RiscvIntcDomainOps, 64)
|
||||
.create_and_add_linear(
|
||||
"riscv-intc".to_string(),
|
||||
&RiscvIntcDomainOps,
|
||||
RiscvIntcChip::IRQ_SIZE,
|
||||
)
|
||||
.ok_or_else(|| {
|
||||
kerror!("Failed to create riscv-intc domain");
|
||||
SystemError::ENXIO
|
||||
@ -152,7 +162,7 @@ pub unsafe fn riscv_intc_init() -> Result<(), SystemError> {
|
||||
irq_domain_manager().set_default_domain(intc_domain.clone());
|
||||
|
||||
unsafe {
|
||||
RISCV_INTC_DOMAIN = Some(intc_domain);
|
||||
RISCV_INTC_DOMAIN = Some(intc_domain.clone());
|
||||
}
|
||||
|
||||
riscv_sbi_timer_irq_desc_init();
|
||||
@ -160,12 +170,58 @@ pub unsafe fn riscv_intc_init() -> Result<(), SystemError> {
|
||||
return Ok(());
|
||||
}
|
||||
|
||||
/// 把硬件中断号转换为riscv intc芯片的中断域的虚拟中断号
|
||||
pub const fn riscv_intc_hwirq_to_virq(hwirq: HardwareIrqNumber) -> Option<IrqNumber> {
|
||||
if hwirq.data() < RiscvIntcChip::IRQ_SIZE {
|
||||
Some(IrqNumber::new(hwirq.data() + RISCV_INTC_VIRQ_START))
|
||||
} else {
|
||||
None
|
||||
}
|
||||
}
|
||||
|
||||
/// 把riscv intc芯片的的中断域的虚拟中断号转换为硬件中断号
|
||||
#[allow(dead_code)]
|
||||
pub const fn riscv_intc_virq_to_hwirq(virq: IrqNumber) -> Option<HardwareIrqNumber> {
|
||||
if virq.data() >= RISCV_INTC_VIRQ_START
|
||||
&& virq.data() < RISCV_INTC_VIRQ_START + RiscvIntcChip::IRQ_SIZE
|
||||
{
|
||||
Some(HardwareIrqNumber::new(virq.data() - RISCV_INTC_VIRQ_START))
|
||||
} else {
|
||||
None
|
||||
}
|
||||
}
|
||||
|
||||
/// 将硬件中断号与riscv intc芯片的虚拟中断号关联
|
||||
pub fn riscv_intc_assicate_irq(hwirq: HardwareIrqNumber) -> Option<IrqNumber> {
|
||||
let virq = riscv_intc_hwirq_to_virq(hwirq)?;
|
||||
irq_domain_manager()
|
||||
.domain_associate(
|
||||
riscv_intc_domain().as_ref().or_else(|| {
|
||||
kerror!("riscv_intc_domain is None");
|
||||
None
|
||||
})?,
|
||||
virq,
|
||||
hwirq,
|
||||
)
|
||||
.ok();
|
||||
|
||||
Some(virq)
|
||||
}
|
||||
|
||||
/// 参考 https://code.dragonos.org.cn/xref/linux-6.6.21/drivers/irqchip/irq-riscv-intc.c#23
|
||||
pub fn riscv_intc_irq(trap_frame: &mut TrapFrame) {
|
||||
let hwirq = HardwareIrqNumber::new(trap_frame.cause.code() as u32);
|
||||
// kdebug!("riscv64_do_irq: interrupt {hwirq:?}");
|
||||
GenericIrqHandler::handle_domain_irq(riscv_intc_domain().clone().unwrap(), hwirq, trap_frame)
|
||||
if hwirq.data() == 9 {
|
||||
// external interrupt
|
||||
do_plic_irq(trap_frame);
|
||||
} else {
|
||||
GenericIrqHandler::handle_domain_irq(
|
||||
riscv_intc_domain().clone().unwrap(),
|
||||
hwirq,
|
||||
trap_frame,
|
||||
)
|
||||
.ok();
|
||||
}
|
||||
do_softirq();
|
||||
if hwirq.data() == RiscVSbiTimer::TIMER_IRQ.data() {
|
||||
__schedule(SchedMode::SM_PREEMPT);
|
||||
|
658
kernel/src/driver/irqchip/riscv_sifive_plic.rs
Normal file
658
kernel/src/driver/irqchip/riscv_sifive_plic.rs
Normal file
@ -0,0 +1,658 @@
|
||||
//! 参考 https://code.dragonos.org.cn/xref/linux-6.6.21/drivers/irqchip/irq-sifive-plic.c
|
||||
//!
|
||||
//!
|
||||
//!
|
||||
//! This driver implements a version of the RISC-V PLIC with the actual layout
|
||||
//! specified in chapter 8 of the SiFive U5 Coreplex Series Manual:
|
||||
//!
|
||||
//! https://static.dev.sifive.com/U54-MC-RVCoreIP.pdf
|
||||
//!
|
||||
//! The largest number supported by devices marked as 'sifive,plic-1.0.0', is
|
||||
//! 1024, of which device 0 is defined as non-existent by the RISC-V Privileged
|
||||
//! Spec.
|
||||
//!
|
||||
|
||||
use core::{
|
||||
fmt::Debug,
|
||||
ops::Deref,
|
||||
ptr::{read_volatile, write_volatile},
|
||||
sync::atomic::AtomicBool,
|
||||
};
|
||||
|
||||
use alloc::{
|
||||
string::ToString,
|
||||
sync::{Arc, Weak},
|
||||
};
|
||||
use bitmap::AllocBitmap;
|
||||
use fdt::node::FdtNode;
|
||||
use system_error::SystemError;
|
||||
|
||||
use crate::{
|
||||
arch::interrupt::TrapFrame,
|
||||
driver::open_firmware::fdt::open_firmware_fdt_driver,
|
||||
exception::{
|
||||
handle::fast_eoi_irq_handler,
|
||||
irqchip::{IrqChip, IrqChipData, IrqChipFlags, IrqChipSetMaskResult},
|
||||
irqdata::IrqData,
|
||||
irqdesc::{irq_desc_manager, GenericIrqHandler},
|
||||
irqdomain::{irq_domain_manager, IrqDomain, IrqDomainOps},
|
||||
manage::irq_manager,
|
||||
HardwareIrqNumber, IrqNumber,
|
||||
},
|
||||
libs::{
|
||||
cpumask::CpuMask,
|
||||
once::Once,
|
||||
spinlock::{SpinLock, SpinLockGuard},
|
||||
},
|
||||
mm::{
|
||||
mmio_buddy::{mmio_pool, MMIOSpaceGuard},
|
||||
percpu::{PerCpu, PerCpuVar},
|
||||
PhysAddr, VirtAddr,
|
||||
},
|
||||
smp::cpu::{smp_cpu_manager, ProcessorId},
|
||||
};
|
||||
|
||||
static mut PLIC_HANDLERS: Option<PerCpuVar<PlicHandler>> = None;
|
||||
|
||||
static mut PLIC_IRQ_CHIP: Option<Arc<PlicIrqChip>> = None;
|
||||
|
||||
#[inline(always)]
|
||||
fn plic_irq_chip() -> Arc<PlicIrqChip> {
|
||||
unsafe { PLIC_IRQ_CHIP.as_ref().unwrap().clone() }
|
||||
}
|
||||
|
||||
#[inline(always)]
|
||||
fn plic_handlers() -> &'static PerCpuVar<PlicHandler> {
|
||||
unsafe { PLIC_HANDLERS.as_ref().unwrap() }
|
||||
}
|
||||
|
||||
#[allow(dead_code)]
|
||||
struct PlicChipData {
|
||||
irq_domain: Weak<IrqDomain>,
|
||||
phandle: u32,
|
||||
lmask: SpinLock<CpuMask>,
|
||||
mmio_guard: Option<MMIOSpaceGuard>,
|
||||
regs: VirtAddr,
|
||||
}
|
||||
|
||||
impl PlicChipData {
|
||||
fn new(
|
||||
irq_domain: Weak<IrqDomain>,
|
||||
mmio_guard: MMIOSpaceGuard,
|
||||
regs: VirtAddr,
|
||||
phandle: u32,
|
||||
) -> Arc<Self> {
|
||||
let r = Self {
|
||||
irq_domain,
|
||||
lmask: SpinLock::new(CpuMask::new()),
|
||||
mmio_guard: Some(mmio_guard),
|
||||
regs,
|
||||
phandle,
|
||||
};
|
||||
|
||||
Arc::new(r)
|
||||
}
|
||||
|
||||
fn lmask(&self) -> SpinLockGuard<CpuMask> {
|
||||
self.lmask.lock()
|
||||
}
|
||||
}
|
||||
|
||||
impl Debug for PlicChipData {
|
||||
fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
|
||||
f.debug_struct("PlicChipData").finish()
|
||||
}
|
||||
}
|
||||
|
||||
impl IrqChipData for PlicChipData {
|
||||
fn as_any_ref(&self) -> &dyn core::any::Any {
|
||||
self
|
||||
}
|
||||
}
|
||||
|
||||
struct PlicHandler {
|
||||
priv_data: Option<Arc<PlicChipData>>,
|
||||
present: AtomicBool,
|
||||
inner: SpinLock<InnerPlicHandler>,
|
||||
}
|
||||
|
||||
struct InnerPlicHandler {
|
||||
hart_base: VirtAddr,
|
||||
enable_base: VirtAddr,
|
||||
enable_save: Option<AllocBitmap>,
|
||||
}
|
||||
|
||||
impl PlicHandler {
|
||||
fn new() -> Self {
|
||||
let inner = InnerPlicHandler {
|
||||
hart_base: VirtAddr::new(0),
|
||||
enable_base: VirtAddr::new(0),
|
||||
enable_save: None,
|
||||
};
|
||||
Self {
|
||||
priv_data: None,
|
||||
present: AtomicBool::new(false),
|
||||
inner: SpinLock::new(inner),
|
||||
}
|
||||
}
|
||||
|
||||
fn set_threshold(&self, threshold: u32) {
|
||||
let inner = self.inner();
|
||||
unsafe {
|
||||
/* priority must be > threshold to trigger an interrupt */
|
||||
core::ptr::write_volatile(
|
||||
(inner.hart_base + PlicIrqChip::CONTEXT_THRESHOLD).data() as *mut u32,
|
||||
threshold,
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
unsafe fn force_set_priv_data(&mut self, priv_data: Arc<PlicChipData>) {
|
||||
self.priv_data = Some(priv_data);
|
||||
}
|
||||
|
||||
fn priv_data(&self) -> Option<Arc<PlicChipData>> {
|
||||
self.priv_data.clone()
|
||||
}
|
||||
|
||||
fn present(&self) -> bool {
|
||||
self.present.load(core::sync::atomic::Ordering::SeqCst)
|
||||
}
|
||||
|
||||
fn set_present(&self, present: bool) {
|
||||
self.present
|
||||
.store(present, core::sync::atomic::Ordering::SeqCst);
|
||||
}
|
||||
|
||||
fn inner(&self) -> SpinLockGuard<InnerPlicHandler> {
|
||||
self.inner.lock()
|
||||
}
|
||||
|
||||
fn toggle(&self, hwirq: HardwareIrqNumber, enable: bool) {
|
||||
let inner = self.inner();
|
||||
let reg = (inner.enable_base + ((hwirq.data() / 32) * 4) as usize).data() as *mut u32;
|
||||
let hwirq_mask = 1 << (hwirq.data() % 32);
|
||||
|
||||
if enable {
|
||||
unsafe {
|
||||
core::ptr::write_volatile(reg, core::ptr::read_volatile(reg) | hwirq_mask);
|
||||
}
|
||||
} else {
|
||||
unsafe {
|
||||
core::ptr::write_volatile(reg, core::ptr::read_volatile(reg) & !hwirq_mask);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn plic_irq_toggle(cpumask: &CpuMask, irq_data: &Arc<IrqData>, enable: bool) {
|
||||
cpumask.iter_cpu().for_each(|cpu| {
|
||||
kdebug!("plic: irq_toggle: cpu: {cpu:?}");
|
||||
let handler = unsafe { plic_handlers().force_get(cpu) };
|
||||
handler.toggle(irq_data.hardware_irq(), enable);
|
||||
});
|
||||
}
|
||||
|
||||
/// SiFive PLIC中断控制器
|
||||
///
|
||||
/// https://code.dragonos.org.cn/xref/linux-6.6.21/drivers/irqchip/irq-sifive-plic.c#204
|
||||
#[derive(Debug)]
|
||||
struct PlicIrqChip;
|
||||
#[allow(dead_code)]
|
||||
impl PlicIrqChip {
|
||||
const COMPATIBLE: &'static str = "sifive,plic-1.0.0";
|
||||
|
||||
const MAX_DEVICES: u32 = 1024;
|
||||
const MAX_CONTEXTS: u32 = 15872;
|
||||
|
||||
/*
|
||||
* Each interrupt source has a priority register associated with it.
|
||||
* We always hardwire it to one in Linux.
|
||||
*/
|
||||
const PRIORITY_BASE: usize = 0;
|
||||
const PRIORITY_PER_ID: usize = 4;
|
||||
|
||||
/*
|
||||
* Each hart context has a vector of interrupt enable bits associated with it.
|
||||
* There's one bit for each interrupt source.
|
||||
*/
|
||||
const CONTEXT_ENABLE_BASE: usize = 0x2080;
|
||||
const CONTEXT_ENABLE_SIZE: usize = 0x100;
|
||||
|
||||
/*
|
||||
* Each hart context has a set of control registers associated with it. Right
|
||||
* now there's only two: a source priority threshold over which the hart will
|
||||
* take an interrupt, and a register to claim interrupts.
|
||||
*/
|
||||
const CONTEXT_BASE: usize = 0x201000;
|
||||
const CONTEXT_SIZE: usize = 0x2000;
|
||||
const CONTEXT_THRESHOLD: usize = 0x00;
|
||||
const CONTEXT_CLAIM: usize = 0x04;
|
||||
|
||||
const PLIC_DISABLE_THRESHOLD: u32 = 0x7;
|
||||
const PLIC_ENABLE_THRESHOLD: u32 = 0;
|
||||
|
||||
const PLIC_QUIRK_EDGE_INTERRUPT: u32 = 0;
|
||||
}
|
||||
|
||||
impl IrqChip for PlicIrqChip {
|
||||
fn name(&self) -> &'static str {
|
||||
"SiFive PLIC"
|
||||
}
|
||||
fn irq_enable(&self, irq_data: &Arc<IrqData>) -> Result<(), SystemError> {
|
||||
// kwarn!("plic: irq_enable");
|
||||
let common_data = irq_data.common_data();
|
||||
let inner_guard = common_data.inner();
|
||||
let mask = inner_guard.effective_affinity();
|
||||
|
||||
plic_irq_toggle(mask, irq_data, true);
|
||||
self.irq_unmask(irq_data).expect("irq_unmask failed");
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn irq_unmask(&self, irq_data: &Arc<IrqData>) -> Result<(), SystemError> {
|
||||
// kwarn!("plic: irq_unmask");
|
||||
|
||||
let chip_data = irq_data
|
||||
.chip_info_read_irqsave()
|
||||
.chip_data()
|
||||
.ok_or(SystemError::EINVAL)?;
|
||||
let plic_chip_data = chip_data
|
||||
.as_any_ref()
|
||||
.downcast_ref::<PlicChipData>()
|
||||
.ok_or(SystemError::EINVAL)?;
|
||||
|
||||
unsafe {
|
||||
core::ptr::write_volatile(
|
||||
(plic_chip_data.regs
|
||||
+ PlicIrqChip::PRIORITY_BASE
|
||||
+ irq_data.hardware_irq().data() as usize * PlicIrqChip::PRIORITY_PER_ID)
|
||||
.data() as *mut u32,
|
||||
1,
|
||||
);
|
||||
}
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn irq_mask(&self, irq_data: &Arc<IrqData>) -> Result<(), SystemError> {
|
||||
let chip_data = irq_data
|
||||
.chip_info_read_irqsave()
|
||||
.chip_data()
|
||||
.ok_or(SystemError::EINVAL)?;
|
||||
let plic_chip_data = chip_data
|
||||
.as_any_ref()
|
||||
.downcast_ref::<PlicChipData>()
|
||||
.ok_or(SystemError::EINVAL)?;
|
||||
|
||||
unsafe {
|
||||
core::ptr::write_volatile(
|
||||
(plic_chip_data.regs
|
||||
+ PlicIrqChip::PRIORITY_BASE
|
||||
+ irq_data.hardware_irq().data() as usize * PlicIrqChip::PRIORITY_PER_ID)
|
||||
.data() as *mut u32,
|
||||
0,
|
||||
);
|
||||
}
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn irq_disable(&self, irq_data: &Arc<IrqData>) {
|
||||
kdebug!("plic: irq_disable");
|
||||
let common_data = irq_data.common_data();
|
||||
let inner_guard = common_data.inner();
|
||||
let mask = inner_guard.effective_affinity();
|
||||
plic_irq_toggle(mask, irq_data, false);
|
||||
}
|
||||
|
||||
fn irq_eoi(&self, irq_data: &Arc<IrqData>) {
|
||||
let handler = plic_handlers().get();
|
||||
|
||||
if core::intrinsics::unlikely(irq_data.common_data().disabled()) {
|
||||
handler.toggle(irq_data.hardware_irq(), true);
|
||||
unsafe {
|
||||
write_volatile(
|
||||
(handler.inner().hart_base + PlicIrqChip::CONTEXT_CLAIM).data() as *mut u32,
|
||||
irq_data.hardware_irq().data(),
|
||||
);
|
||||
}
|
||||
|
||||
handler.toggle(irq_data.hardware_irq(), false);
|
||||
} else {
|
||||
// kdebug!("plic: irq_eoi: hwirq: {:?}", irq_data.hardware_irq());
|
||||
unsafe {
|
||||
write_volatile(
|
||||
(handler.inner().hart_base + PlicIrqChip::CONTEXT_CLAIM).data() as *mut u32,
|
||||
irq_data.hardware_irq().data(),
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn irq_ack(&self, _irq: &Arc<IrqData>) {
|
||||
todo!()
|
||||
}
|
||||
|
||||
fn can_mask_ack(&self) -> bool {
|
||||
false
|
||||
}
|
||||
|
||||
fn can_set_affinity(&self) -> bool {
|
||||
true
|
||||
}
|
||||
|
||||
/// 设置中断的亲和性
|
||||
///
|
||||
/// 参考 https://code.dragonos.org.cn/xref/linux-6.6.21/drivers/irqchip/irq-sifive-plic.c#161
|
||||
fn irq_set_affinity(
|
||||
&self,
|
||||
irq_data: &Arc<IrqData>,
|
||||
mask_val: &CpuMask,
|
||||
force: bool,
|
||||
) -> Result<IrqChipSetMaskResult, SystemError> {
|
||||
let chip_data = irq_data
|
||||
.chip_info_read_irqsave()
|
||||
.chip_data()
|
||||
.ok_or(SystemError::EINVAL)?;
|
||||
let plic_chip_data = chip_data
|
||||
.as_any_ref()
|
||||
.downcast_ref::<PlicChipData>()
|
||||
.ok_or(SystemError::EINVAL)?;
|
||||
|
||||
let mut amask = plic_chip_data.lmask().deref() & mask_val;
|
||||
let cpu = if force {
|
||||
mask_val.first()
|
||||
} else {
|
||||
amask.bitand_assign(smp_cpu_manager().possible_cpus());
|
||||
// todo: 随机选择一个CPU
|
||||
amask.first()
|
||||
}
|
||||
.ok_or(SystemError::EINVAL)?;
|
||||
|
||||
if cpu.data() > smp_cpu_manager().present_cpus_count() {
|
||||
return Err(SystemError::EINVAL);
|
||||
}
|
||||
|
||||
self.irq_disable(irq_data);
|
||||
irq_data
|
||||
.common_data()
|
||||
.set_effective_affinity(CpuMask::from_cpu(cpu));
|
||||
if !irq_data.common_data().disabled() {
|
||||
self.irq_enable(irq_data).ok();
|
||||
}
|
||||
|
||||
Ok(IrqChipSetMaskResult::Done)
|
||||
}
|
||||
|
||||
fn can_set_flow_type(&self) -> bool {
|
||||
false
|
||||
}
|
||||
|
||||
fn flags(&self) -> IrqChipFlags {
|
||||
IrqChipFlags::empty()
|
||||
}
|
||||
}
|
||||
|
||||
#[inline(never)]
|
||||
pub fn riscv_sifive_plic_init() -> Result<(), SystemError> {
|
||||
static INIT_PLIC_IRQ_CHIP_ONCE: Once = Once::new();
|
||||
INIT_PLIC_IRQ_CHIP_ONCE.call_once(|| unsafe {
|
||||
PLIC_IRQ_CHIP = Some(Arc::new(PlicIrqChip));
|
||||
|
||||
PLIC_HANDLERS = Some(
|
||||
PerCpuVar::new(
|
||||
(0..PerCpu::MAX_CPU_NUM)
|
||||
.map(|_| PlicHandler::new())
|
||||
.collect(),
|
||||
)
|
||||
.unwrap(),
|
||||
);
|
||||
});
|
||||
|
||||
let fdt = open_firmware_fdt_driver().fdt_ref()?;
|
||||
let all_plics = fdt.all_nodes().filter(|x| {
|
||||
if let Some(compatible) = x.compatible() {
|
||||
compatible
|
||||
.all()
|
||||
.any(|x| x == PlicIrqChip::COMPATIBLE || x == "riscv,plic0")
|
||||
} else {
|
||||
false
|
||||
}
|
||||
});
|
||||
for node in all_plics {
|
||||
if let Err(e) = do_riscv_sifive_plic_init(&node) {
|
||||
kwarn!("Failed to init SiFive PLIC: node: {node:?} {e:?}");
|
||||
}
|
||||
}
|
||||
|
||||
unsafe { riscv::register::sie::set_sext() };
|
||||
Ok(())
|
||||
}
|
||||
|
||||
/// 初始化SiFive PLIC
|
||||
///
|
||||
/// 参考 https://code.dragonos.org.cn/xref/linux-6.6.21/drivers/irqchip/irq-sifive-plic.c#415
|
||||
fn do_riscv_sifive_plic_init(fdt_node: &FdtNode) -> Result<(), SystemError> {
|
||||
let reg = fdt_node
|
||||
.reg()
|
||||
.ok_or(SystemError::EINVAL)?
|
||||
.next()
|
||||
.ok_or(SystemError::EIO)?;
|
||||
let paddr = PhysAddr::new(reg.starting_address as usize);
|
||||
let size = reg.size.ok_or(SystemError::EINVAL)?;
|
||||
let mmio_guard = mmio_pool().create_mmio(size)?;
|
||||
let vaddr = unsafe { mmio_guard.map_any_phys(paddr, size) }?;
|
||||
|
||||
let phandle = fdt_node
|
||||
.property("phandle")
|
||||
.ok_or(SystemError::EINVAL)?
|
||||
.as_usize()
|
||||
.ok_or(SystemError::EINVAL)?;
|
||||
|
||||
// 中断数量
|
||||
let irq_num = fdt_node
|
||||
.property("riscv,ndev")
|
||||
.ok_or(SystemError::EINVAL)?
|
||||
.as_usize()
|
||||
.ok_or(SystemError::EINVAL)?;
|
||||
kdebug!(
|
||||
"plic: node: {}, irq_num: {irq_num}, paddr: {paddr:?}, size: {size}",
|
||||
fdt_node.name
|
||||
);
|
||||
let nr_contexts = fdt_node
|
||||
.interrupts_extended()
|
||||
.ok_or(SystemError::EINVAL)?
|
||||
.count();
|
||||
kdebug!("plic: nr_contexts: {nr_contexts}");
|
||||
|
||||
let irq_domain = irq_domain_manager()
|
||||
.create_and_add_linear(
|
||||
fdt_node.name.to_string(),
|
||||
&PlicIrqDomainOps,
|
||||
(irq_num + 1) as u32,
|
||||
)
|
||||
.ok_or(SystemError::EINVAL)?;
|
||||
// kdebug!("plic: irq_domain: {irq_domain:?}");
|
||||
|
||||
let priv_data = PlicChipData::new(
|
||||
Arc::downgrade(&irq_domain),
|
||||
mmio_guard,
|
||||
vaddr,
|
||||
phandle as u32,
|
||||
);
|
||||
irq_domain.set_host_data(Some(priv_data.clone() as Arc<dyn IrqChipData>));
|
||||
|
||||
let loop_done_setup = |irq_handler: &PlicHandler| {
|
||||
for x in 1..=irq_num {
|
||||
irq_handler.toggle(HardwareIrqNumber::new(x as u32), false);
|
||||
|
||||
unsafe {
|
||||
core::ptr::write_volatile(
|
||||
(priv_data.regs + PlicIrqChip::PRIORITY_BASE + x * PlicIrqChip::PRIORITY_PER_ID)
|
||||
.data() as *mut u32,
|
||||
1,
|
||||
)
|
||||
};
|
||||
}
|
||||
};
|
||||
|
||||
// todo: 学习linux那样处理,获取到hartid,这里暂时糊代码
|
||||
// linux: https://code.dragonos.org.cn/xref/linux-6.6.21/drivers/irqchip/irq-sifive-plic.c#458
|
||||
for i in smp_cpu_manager().present_cpus().iter_cpu() {
|
||||
let i = i.data() as usize;
|
||||
|
||||
let cpu = ProcessorId::new(i as u32);
|
||||
let handler = unsafe { plic_handlers().force_get(cpu) };
|
||||
if handler.present() {
|
||||
kwarn!("plic: handler {i} already present.");
|
||||
handler.set_threshold(PlicIrqChip::PLIC_ENABLE_THRESHOLD);
|
||||
loop_done_setup(handler);
|
||||
continue;
|
||||
}
|
||||
|
||||
kdebug!("plic: setup lmask {cpu:?}.");
|
||||
priv_data.lmask().set(cpu, true);
|
||||
let mut handler_inner = handler.inner();
|
||||
handler_inner.hart_base =
|
||||
priv_data.regs + PlicIrqChip::CONTEXT_BASE + i * PlicIrqChip::CONTEXT_SIZE;
|
||||
handler_inner.enable_base = priv_data.regs
|
||||
+ PlicIrqChip::CONTEXT_ENABLE_BASE
|
||||
+ i * PlicIrqChip::CONTEXT_ENABLE_SIZE;
|
||||
handler.set_present(true);
|
||||
unsafe {
|
||||
plic_handlers()
|
||||
.force_get_mut(cpu)
|
||||
.force_set_priv_data(priv_data.clone())
|
||||
};
|
||||
|
||||
handler_inner.enable_save = Some(AllocBitmap::new(irq_num as usize));
|
||||
|
||||
drop(handler_inner);
|
||||
handler.set_threshold(PlicIrqChip::PLIC_ENABLE_THRESHOLD);
|
||||
|
||||
loop_done_setup(handler);
|
||||
}
|
||||
|
||||
// 把外部设备的中断与PLIC关联起来
|
||||
associate_irq_with_plic_domain(&irq_domain, phandle as u32).ok();
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
/// 把设备的中断与PLIC的关联起来
|
||||
fn associate_irq_with_plic_domain(
|
||||
irq_domain: &Arc<IrqDomain>,
|
||||
plic_phandle: u32,
|
||||
) -> Result<(), SystemError> {
|
||||
let fdt_ref = open_firmware_fdt_driver().fdt_ref()?;
|
||||
let nodes = fdt_ref.all_nodes().filter(|x| {
|
||||
if let Some(pa) = x.property("interrupt-parent").and_then(|x| x.as_usize()) {
|
||||
pa as u32 == plic_phandle
|
||||
} else {
|
||||
false
|
||||
}
|
||||
});
|
||||
|
||||
for node in nodes {
|
||||
if let Some(irq) = node.interrupts().and_then(|mut x| x.next()) {
|
||||
let irq = irq as u32;
|
||||
let virq = IrqNumber::new(irq);
|
||||
let hwirq = HardwareIrqNumber::new(irq);
|
||||
kdebug!("plic: associate irq: {irq}, virq: {virq:?}, hwirq: {hwirq:?}");
|
||||
irq_domain_manager()
|
||||
.domain_associate(irq_domain, virq, hwirq)
|
||||
.ok();
|
||||
}
|
||||
}
|
||||
|
||||
Ok(())
|
||||
}
|
||||
#[derive(Debug)]
|
||||
struct PlicIrqDomainOps;
|
||||
|
||||
impl IrqDomainOps for PlicIrqDomainOps {
|
||||
fn unmap(&self, _irq_domain: &Arc<IrqDomain>, _virq: IrqNumber) {
|
||||
todo!()
|
||||
}
|
||||
|
||||
fn map(
|
||||
&self,
|
||||
irq_domain: &Arc<IrqDomain>,
|
||||
hwirq: HardwareIrqNumber,
|
||||
virq: IrqNumber,
|
||||
) -> Result<(), SystemError> {
|
||||
// kdebug!("plic: map: virq: {virq:?}, hwirq: {hwirq:?}");
|
||||
|
||||
let chip_data = irq_domain.host_data().ok_or(SystemError::EINVAL)?;
|
||||
let plic_chip_data = chip_data
|
||||
.as_any_ref()
|
||||
.downcast_ref::<PlicChipData>()
|
||||
.ok_or(SystemError::EINVAL)?;
|
||||
irq_domain_manager().domain_set_info(
|
||||
irq_domain,
|
||||
virq,
|
||||
hwirq,
|
||||
plic_irq_chip(),
|
||||
irq_domain.host_data(),
|
||||
fast_eoi_irq_handler(),
|
||||
None,
|
||||
None,
|
||||
);
|
||||
let irq_desc = irq_desc_manager().lookup(virq).unwrap();
|
||||
irq_desc.set_noprobe();
|
||||
let mask = plic_chip_data.lmask().clone();
|
||||
irq_manager().irq_set_affinity(&irq_desc.irq_data(), &irq_desc.inner(), &mask)?;
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn activate(
|
||||
&self,
|
||||
_domain: &Arc<IrqDomain>,
|
||||
_irq_data: &Arc<IrqData>,
|
||||
_reserve: bool,
|
||||
) -> Result<(), SystemError> {
|
||||
kwarn!("plic: activate");
|
||||
loop {}
|
||||
}
|
||||
|
||||
fn deactivate(&self, _domain: &Arc<IrqDomain>, _irq_data: &Arc<IrqData>) {}
|
||||
}
|
||||
|
||||
/// 处理PLIC中断
|
||||
pub(super) fn do_plic_irq(trap_frame: &mut TrapFrame) {
|
||||
// kdebug!("plic: do_plic_irq");
|
||||
|
||||
let handler = plic_handlers().get();
|
||||
let priv_data = handler.priv_data();
|
||||
if priv_data.is_none() {
|
||||
return;
|
||||
}
|
||||
|
||||
let domain = priv_data.unwrap().irq_domain.upgrade();
|
||||
if domain.is_none() {
|
||||
return;
|
||||
}
|
||||
|
||||
let domain = domain.unwrap();
|
||||
|
||||
// 循环处理中断
|
||||
loop {
|
||||
let claim = unsafe {
|
||||
read_volatile(
|
||||
(handler.inner().hart_base + PlicIrqChip::CONTEXT_CLAIM).data() as *const u32,
|
||||
)
|
||||
};
|
||||
|
||||
if claim == 0 {
|
||||
break;
|
||||
}
|
||||
kdebug!("plic: claim: {claim:?}");
|
||||
|
||||
let hwirq = HardwareIrqNumber::new(claim);
|
||||
if let Err(e) = GenericIrqHandler::handle_domain_irq(domain.clone(), hwirq, trap_frame) {
|
||||
kwarn!("plic: can't find mapping for hwirq {hwirq:?}, {e:?}");
|
||||
}
|
||||
}
|
||||
}
|
@ -207,6 +207,10 @@ impl VirtIODevice for VirtioInterface {
|
||||
return Ok(IrqReturn::Handled);
|
||||
}
|
||||
|
||||
fn irq(&self) -> Option<IrqNumber> {
|
||||
None
|
||||
}
|
||||
|
||||
fn dev_id(&self) -> &Arc<DeviceId> {
|
||||
return &self.dev_id;
|
||||
}
|
||||
|
@ -3,7 +3,16 @@ use hashbrown::HashMap;
|
||||
use system_error::SystemError;
|
||||
use unified_init::macros::unified_init;
|
||||
|
||||
use crate::{driver::base::device::DeviceId, init::initcall::INITCALL_CORE, libs::rwlock::RwLock};
|
||||
use crate::{
|
||||
driver::base::device::DeviceId,
|
||||
exception::{
|
||||
irqdata::IrqHandlerData,
|
||||
irqdesc::{IrqHandler, IrqReturn},
|
||||
IrqNumber,
|
||||
},
|
||||
init::initcall::INITCALL_CORE,
|
||||
libs::rwlock::RwLock,
|
||||
};
|
||||
|
||||
use super::VirtIODevice;
|
||||
|
||||
@ -83,3 +92,36 @@ fn init_virtio_irq_manager() -> Result<(), SystemError> {
|
||||
}
|
||||
return Ok(());
|
||||
}
|
||||
|
||||
/// `DefaultVirtioIrqHandler` 是一个默认的virtio设备中断处理程序。
|
||||
///
|
||||
/// 当虚拟设备产生中断时,该处理程序会被调用。
|
||||
///
|
||||
/// 它首先检查设备ID是否存在,然后尝试查找与设备ID关联的设备。
|
||||
/// 如果找到设备,它会调用设备的 `handle_irq` 方法来处理中断。
|
||||
/// 如果没有找到设备,它会记录一条警告并返回 `IrqReturn::NotHandled`,表示中断未被处理。
|
||||
#[derive(Debug)]
|
||||
pub(super) struct DefaultVirtioIrqHandler;
|
||||
|
||||
impl IrqHandler for DefaultVirtioIrqHandler {
|
||||
fn handle(
|
||||
&self,
|
||||
irq: IrqNumber,
|
||||
_static_data: Option<&dyn IrqHandlerData>,
|
||||
dev_id: Option<Arc<dyn IrqHandlerData>>,
|
||||
) -> Result<IrqReturn, SystemError> {
|
||||
let dev_id = dev_id.ok_or(SystemError::EINVAL)?;
|
||||
let dev_id = dev_id
|
||||
.arc_any()
|
||||
.downcast::<DeviceId>()
|
||||
.map_err(|_| SystemError::EINVAL)?;
|
||||
|
||||
if let Some(dev) = virtio_irq_manager().lookup_device(&dev_id) {
|
||||
return dev.handle_irq(irq);
|
||||
} else {
|
||||
// 未绑定具体设备,因此无法处理中断
|
||||
kwarn!("No device found for IRQ: {:?}", irq);
|
||||
return Ok(IrqReturn::NotHandled);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -36,6 +36,13 @@ pub trait VirtIODevice: Device {
|
||||
|
||||
/// virtio 设备厂商
|
||||
fn vendor(&self) -> u32;
|
||||
|
||||
/// virtio设备的中断号
|
||||
fn irq(&self) -> Option<IrqNumber>;
|
||||
|
||||
fn set_irq_number(&self, _irq: IrqNumber) -> Result<(), SystemError> {
|
||||
Err(SystemError::ENOSYS)
|
||||
}
|
||||
}
|
||||
|
||||
pub trait VirtIODriver: Driver {
|
||||
|
@ -8,16 +8,20 @@ use system_error::SystemError;
|
||||
use unified_init::macros::unified_init;
|
||||
|
||||
use crate::{
|
||||
driver::base::{
|
||||
device::{
|
||||
bus::{bus_manager, Bus},
|
||||
device_manager,
|
||||
driver::{driver_manager, Driver},
|
||||
Device,
|
||||
driver::{
|
||||
base::{
|
||||
device::{
|
||||
bus::{bus_manager, Bus},
|
||||
device_manager,
|
||||
driver::{driver_manager, Driver},
|
||||
Device,
|
||||
},
|
||||
kobject::KObject,
|
||||
subsys::SubSysPrivate,
|
||||
},
|
||||
kobject::KObject,
|
||||
subsys::SubSysPrivate,
|
||||
virtio::irq::{virtio_irq_manager, DefaultVirtioIrqHandler},
|
||||
},
|
||||
exception::{irqdesc::IrqHandleFlags, manage::irq_manager},
|
||||
filesystem::{
|
||||
sysfs::{
|
||||
file::sysfs_emit_str, Attribute, AttributeGroup, SysFSOpsSupport, SYSFS_ATTR_MODE_RO,
|
||||
@ -175,7 +179,47 @@ impl VirtIODeviceManager {
|
||||
virtio_drv.probe(&dev)?;
|
||||
|
||||
device_manager().add_device(dev.clone() as Arc<dyn Device>)?;
|
||||
device_manager().add_groups(&(dev as Arc<dyn Device>), &[&VirtIODeviceAttrGroup])
|
||||
let r = device_manager()
|
||||
.add_groups(&(dev.clone() as Arc<dyn Device>), &[&VirtIODeviceAttrGroup]);
|
||||
|
||||
self.setup_irq(&dev).ok();
|
||||
|
||||
return r;
|
||||
}
|
||||
|
||||
/// # setup_irq - 设置中断
|
||||
///
|
||||
/// 为virtio设备设置中断。
|
||||
fn setup_irq(&self, dev: &Arc<dyn VirtIODevice>) -> Result<(), SystemError> {
|
||||
let irq = dev.irq().ok_or(SystemError::EINVAL)?;
|
||||
if let Err(e) = irq_manager().request_irq(
|
||||
irq,
|
||||
dev.device_name(),
|
||||
&DefaultVirtioIrqHandler,
|
||||
IrqHandleFlags::IRQF_SHARED,
|
||||
Some(dev.dev_id().clone()),
|
||||
) {
|
||||
kerror!(
|
||||
"Failed to request irq for virtio device '{}': irq: {:?}, error {:?}",
|
||||
dev.device_name(),
|
||||
irq,
|
||||
e
|
||||
);
|
||||
return Err(e);
|
||||
}
|
||||
|
||||
virtio_irq_manager()
|
||||
.register_device(dev.clone())
|
||||
.map_err(|e| {
|
||||
kerror!(
|
||||
"Failed to register virtio device's irq, dev: '{}', irq: {:?}, error {:?}",
|
||||
dev.device_name(),
|
||||
irq,
|
||||
e
|
||||
);
|
||||
e
|
||||
})?;
|
||||
return Ok(());
|
||||
}
|
||||
|
||||
#[allow(dead_code)]
|
||||
|
@ -1,5 +1,7 @@
|
||||
use virtio_drivers::transport::Transport;
|
||||
|
||||
use crate::exception::HardwareIrqNumber;
|
||||
|
||||
use super::{transport_mmio::VirtIOMmioTransport, transport_pci::PciTransport};
|
||||
|
||||
pub enum VirtIOTransport {
|
||||
@ -7,6 +9,15 @@ pub enum VirtIOTransport {
|
||||
Mmio(VirtIOMmioTransport),
|
||||
}
|
||||
|
||||
impl VirtIOTransport {
|
||||
pub fn irq(&self) -> Option<HardwareIrqNumber> {
|
||||
match self {
|
||||
VirtIOTransport::Mmio(transport) => Some(transport.irq()),
|
||||
_ => None,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl core::fmt::Debug for VirtIOTransport {
|
||||
fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
|
||||
match self {
|
||||
|
@ -8,9 +8,6 @@ use crate::driver::pci::pci::{
|
||||
|
||||
use crate::driver::pci::pci_irq::{IrqCommonMsg, IrqSpecificMsg, PciInterrupt, PciIrqMsg, IRQ};
|
||||
use crate::driver::pci::root::pci_root_0;
|
||||
use crate::driver::virtio::irq::virtio_irq_manager;
|
||||
use crate::exception::irqdata::IrqHandlerData;
|
||||
use crate::exception::irqdesc::{IrqHandler, IrqReturn};
|
||||
|
||||
use crate::exception::IrqNumber;
|
||||
|
||||
@ -26,12 +23,12 @@ use core::{
|
||||
mem::{align_of, size_of},
|
||||
ptr::{self, addr_of_mut, NonNull},
|
||||
};
|
||||
use system_error::SystemError;
|
||||
use virtio_drivers::{
|
||||
transport::{DeviceStatus, DeviceType, Transport},
|
||||
Error, Hal, PhysAddr,
|
||||
};
|
||||
|
||||
use super::irq::DefaultVirtioIrqHandler;
|
||||
use super::VIRTIO_VENDOR_ID;
|
||||
|
||||
/// The offset to add to a VirtIO device ID to get the corresponding PCI device ID.
|
||||
@ -569,36 +566,3 @@ fn get_bar_region_slice<T>(
|
||||
fn nonnull_slice_from_raw_parts<T>(data: NonNull<T>, len: usize) -> NonNull<[T]> {
|
||||
NonNull::new(ptr::slice_from_raw_parts_mut(data.as_ptr(), len)).unwrap()
|
||||
}
|
||||
|
||||
/// `DefaultVirtioIrqHandler` 是一个默认的virtio设备中断处理程序。
|
||||
///
|
||||
/// 当虚拟设备产生中断时,该处理程序会被调用。
|
||||
///
|
||||
/// 它首先检查设备ID是否存在,然后尝试查找与设备ID关联的设备。
|
||||
/// 如果找到设备,它会调用设备的 `handle_irq` 方法来处理中断。
|
||||
/// 如果没有找到设备,它会记录一条警告并返回 `IrqReturn::NotHandled`,表示中断未被处理。
|
||||
#[derive(Debug)]
|
||||
struct DefaultVirtioIrqHandler;
|
||||
|
||||
impl IrqHandler for DefaultVirtioIrqHandler {
|
||||
fn handle(
|
||||
&self,
|
||||
irq: IrqNumber,
|
||||
_static_data: Option<&dyn IrqHandlerData>,
|
||||
dev_id: Option<Arc<dyn IrqHandlerData>>,
|
||||
) -> Result<IrqReturn, SystemError> {
|
||||
let dev_id = dev_id.ok_or(SystemError::EINVAL)?;
|
||||
let dev_id = dev_id
|
||||
.arc_any()
|
||||
.downcast::<DeviceId>()
|
||||
.map_err(|_| SystemError::EINVAL)?;
|
||||
|
||||
if let Some(dev) = virtio_irq_manager().lookup_device(&dev_id) {
|
||||
return dev.handle_irq(irq);
|
||||
} else {
|
||||
// 未绑定具体设备,因此无法处理中断
|
||||
|
||||
return Ok(IrqReturn::NotHandled);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -5,13 +5,14 @@ use system_error::SystemError;
|
||||
|
||||
use crate::{
|
||||
arch::{interrupt::TrapFrame, CurrentIrqArch},
|
||||
exception::irqdesc::InnerIrqDesc,
|
||||
exception::{irqchip::IrqChipFlags, irqdesc::InnerIrqDesc},
|
||||
libs::{once::Once, spinlock::SpinLockGuard},
|
||||
process::{ProcessFlags, ProcessManager},
|
||||
smp::core::smp_get_processor_id,
|
||||
};
|
||||
|
||||
use super::{
|
||||
irqchip::IrqChip,
|
||||
irqdata::{IrqData, IrqHandlerData, IrqStatus},
|
||||
irqdesc::{
|
||||
InnerIrqAction, IrqDesc, IrqDescState, IrqFlowHandler, IrqReturn, ThreadedHandlerFlags,
|
||||
@ -34,6 +35,7 @@ pub fn fast_eoi_irq_handler() -> &'static dyn IrqFlowHandler {
|
||||
|
||||
/// 获取用于处理边沿触发中断的处理程序
|
||||
#[inline(always)]
|
||||
#[allow(dead_code)]
|
||||
pub fn edge_irq_handler() -> &'static dyn IrqFlowHandler {
|
||||
&EdgeIrqHandler
|
||||
}
|
||||
@ -55,9 +57,38 @@ impl IrqFlowHandler for HandleBadIrq {
|
||||
struct FastEOIIrqHandler;
|
||||
|
||||
impl IrqFlowHandler for FastEOIIrqHandler {
|
||||
fn handle(&self, _irq_desc: &Arc<IrqDesc>, _trap_frame: &mut TrapFrame) {
|
||||
// https://code.dragonos.org.cn/xref/linux-6.1.9/kernel/irq/chip.c?r=&mo=17578&fi=689#689
|
||||
todo!("FastEOIIrqHandler");
|
||||
/// https://code.dragonos.org.cn/xref/linux-6.1.9/kernel/irq/chip.c?r=&mo=17578&fi=689#689
|
||||
fn handle(&self, irq_desc: &Arc<IrqDesc>, _trap_frame: &mut TrapFrame) {
|
||||
let chip = irq_desc.irq_data().chip_info_read_irqsave().chip();
|
||||
|
||||
let mut desc_inner = irq_desc.inner();
|
||||
let out = |din: SpinLockGuard<InnerIrqDesc>| {
|
||||
if !chip.flags().contains(IrqChipFlags::IRQCHIP_EOI_IF_HANDLED) {
|
||||
chip.irq_eoi(din.irq_data());
|
||||
}
|
||||
};
|
||||
if !irq_may_run(&desc_inner) {
|
||||
out(desc_inner);
|
||||
return;
|
||||
}
|
||||
|
||||
desc_inner
|
||||
.internal_state_mut()
|
||||
.remove(IrqDescState::IRQS_REPLAY | IrqDescState::IRQS_WAITING);
|
||||
|
||||
if desc_inner.actions().is_empty() || desc_inner.common_data().disabled() {
|
||||
desc_inner
|
||||
.internal_state_mut()
|
||||
.insert(IrqDescState::IRQS_PENDING);
|
||||
mask_irq(desc_inner.irq_data());
|
||||
out(desc_inner);
|
||||
return;
|
||||
}
|
||||
|
||||
desc_inner = handle_irq_event(irq_desc, desc_inner);
|
||||
cond_unmask_eoi_irq(&desc_inner, &chip);
|
||||
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
@ -112,17 +143,7 @@ impl IrqFlowHandler for EdgeIrqHandler {
|
||||
|
||||
// kdebug!("handle_irq_event");
|
||||
|
||||
desc_inner_guard
|
||||
.internal_state_mut()
|
||||
.remove(IrqDescState::IRQS_PENDING);
|
||||
desc_inner_guard.common_data().set_inprogress();
|
||||
|
||||
drop(desc_inner_guard);
|
||||
|
||||
let _r = do_handle_irq_event(irq_desc);
|
||||
|
||||
desc_inner_guard = irq_desc.inner();
|
||||
desc_inner_guard.common_data().clear_inprogress();
|
||||
desc_inner_guard = handle_irq_event(irq_desc, desc_inner_guard);
|
||||
|
||||
if !desc_inner_guard
|
||||
.internal_state()
|
||||
@ -160,6 +181,29 @@ pub(super) fn mask_ack_irq(irq_data: &Arc<IrqData>) {
|
||||
}
|
||||
}
|
||||
|
||||
pub(super) fn mask_irq(irq_data: &Arc<IrqData>) {
|
||||
if irq_data.common_data().masked() {
|
||||
return;
|
||||
}
|
||||
|
||||
let chip = irq_data.chip_info_read_irqsave().chip();
|
||||
if chip.irq_mask(irq_data).is_ok() {
|
||||
irq_data.irqd_set(IrqStatus::IRQD_IRQ_MASKED);
|
||||
}
|
||||
}
|
||||
|
||||
pub(super) fn unmask_irq(irq_data: &Arc<IrqData>) {
|
||||
if !irq_data.common_data().masked() {
|
||||
return;
|
||||
}
|
||||
|
||||
let chip = irq_data.chip_info_read_irqsave().chip();
|
||||
|
||||
if chip.irq_unmask(irq_data).is_ok() {
|
||||
irq_data.irqd_clear(IrqStatus::IRQD_IRQ_MASKED);
|
||||
}
|
||||
}
|
||||
|
||||
impl IrqManager {
|
||||
pub(super) fn do_irq_wake_thread(
|
||||
&self,
|
||||
@ -191,6 +235,24 @@ impl IrqManager {
|
||||
}
|
||||
}
|
||||
|
||||
fn handle_irq_event<'a>(
|
||||
irq_desc: &'a Arc<IrqDesc>,
|
||||
mut desc_inner_guard: SpinLockGuard<'_, InnerIrqDesc>,
|
||||
) -> SpinLockGuard<'a, InnerIrqDesc> {
|
||||
desc_inner_guard
|
||||
.internal_state_mut()
|
||||
.remove(IrqDescState::IRQS_PENDING);
|
||||
desc_inner_guard.common_data().set_inprogress();
|
||||
|
||||
drop(desc_inner_guard);
|
||||
|
||||
let _r = do_handle_irq_event(irq_desc);
|
||||
|
||||
let desc_inner_guard = irq_desc.inner();
|
||||
desc_inner_guard.common_data().clear_inprogress();
|
||||
|
||||
return desc_inner_guard;
|
||||
}
|
||||
/// 处理中断事件
|
||||
///
|
||||
/// https://code.dragonos.org.cn/xref/linux-6.1.9/kernel/irq/handle.c?fi=handle_irq_event#139
|
||||
@ -228,6 +290,47 @@ fn do_handle_irq_event(desc: &Arc<IrqDesc>) -> Result<(), SystemError> {
|
||||
return r.map(|_| ());
|
||||
}
|
||||
|
||||
/// 参考 https://code.dragonos.org.cn/xref/linux-6.1.9/kernel/irq/chip.c?r=&mo=17578&fi=659
|
||||
fn cond_unmask_eoi_irq(
|
||||
desc_inner_guard: &SpinLockGuard<'_, InnerIrqDesc>,
|
||||
chip: &Arc<dyn IrqChip>,
|
||||
) {
|
||||
if !desc_inner_guard
|
||||
.internal_state()
|
||||
.contains(IrqDescState::IRQS_ONESHOT)
|
||||
{
|
||||
chip.irq_eoi(desc_inner_guard.irq_data());
|
||||
return;
|
||||
}
|
||||
|
||||
/*
|
||||
* We need to unmask in the following cases:
|
||||
* - Oneshot irq which did not wake the thread (caused by a
|
||||
* spurious interrupt or a primary handler handling it
|
||||
* completely).
|
||||
*/
|
||||
|
||||
if !desc_inner_guard.common_data().disabled()
|
||||
&& desc_inner_guard.common_data().masked()
|
||||
&& desc_inner_guard.threads_oneshot() == 0
|
||||
{
|
||||
kdebug!(
|
||||
"eoi unmask irq {}",
|
||||
desc_inner_guard.irq_data().irq().data()
|
||||
);
|
||||
chip.irq_eoi(desc_inner_guard.irq_data());
|
||||
unmask_irq(desc_inner_guard.irq_data());
|
||||
} else if !chip.flags().contains(IrqChipFlags::IRQCHIP_EOI_THREADED) {
|
||||
kdebug!("eoi irq {}", desc_inner_guard.irq_data().irq().data());
|
||||
chip.irq_eoi(desc_inner_guard.irq_data());
|
||||
} else {
|
||||
kwarn!(
|
||||
"irq {} eoi failed",
|
||||
desc_inner_guard.irq_data().irq().data()
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
fn warn_no_thread(irq: IrqNumber, action_inner: &mut SpinLockGuard<'_, InnerIrqAction>) {
|
||||
// warn on once
|
||||
if action_inner
|
||||
|
@ -37,27 +37,27 @@ use super::{
|
||||
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> {
|
||||
fn irq_startup(&self, _irq_data: &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> {
|
||||
fn irq_shutdown(&self, _irq_data: &Arc<IrqData>) -> Result<(), SystemError> {
|
||||
Err(SystemError::ENOSYS)
|
||||
}
|
||||
|
||||
/// enable the interrupt
|
||||
///
|
||||
/// (defaults to ->unmask if ENOSYS)
|
||||
fn irq_enable(&self, _irq: &Arc<IrqData>) -> Result<(), SystemError> {
|
||||
fn irq_enable(&self, _irq_data: &Arc<IrqData>) -> Result<(), SystemError> {
|
||||
Err(SystemError::ENOSYS)
|
||||
}
|
||||
|
||||
/// disable the interrupt
|
||||
fn irq_disable(&self, irq: &Arc<IrqData>);
|
||||
fn irq_disable(&self, irq_data: &Arc<IrqData>);
|
||||
|
||||
/// start of a new interrupt
|
||||
fn irq_ack(&self, irq: &Arc<IrqData>);
|
||||
fn irq_ack(&self, irq_data: &Arc<IrqData>);
|
||||
|
||||
/// mask an interrupt source
|
||||
///
|
||||
@ -66,7 +66,7 @@ pub trait IrqChip: Sync + Send + Any + Debug {
|
||||
/// 如果返回ENOSYS,则表明irq_mask()不支持. 那么中断机制代码将调用irq_disable()。
|
||||
///
|
||||
/// 如果返回错误,那么中断的屏蔽状态将不会改变。
|
||||
fn irq_mask(&self, _irq: &Arc<IrqData>) -> Result<(), SystemError> {
|
||||
fn irq_mask(&self, _irq_data: &Arc<IrqData>) -> Result<(), SystemError> {
|
||||
Err(SystemError::ENOSYS)
|
||||
}
|
||||
|
||||
@ -74,18 +74,18 @@ pub trait IrqChip: Sync + Send + Any + Debug {
|
||||
fn can_mask_ack(&self) -> bool;
|
||||
|
||||
/// ack and mask an interrupt source
|
||||
fn irq_mask_ack(&self, _irq: &Arc<IrqData>) {}
|
||||
fn irq_mask_ack(&self, _irq_data: &Arc<IrqData>) {}
|
||||
|
||||
/// unmask an interrupt source
|
||||
///
|
||||
/// 用于取消屏蔽中断
|
||||
///
|
||||
/// 如果返回ENOSYS,则表明irq_unmask()不支持.
|
||||
fn irq_unmask(&self, _irq: &Arc<IrqData>) -> Result<(), SystemError> {
|
||||
fn irq_unmask(&self, _irq_data: &Arc<IrqData>) -> Result<(), SystemError> {
|
||||
Err(SystemError::ENOSYS)
|
||||
}
|
||||
/// end of interrupt
|
||||
fn irq_eoi(&self, _irq: &Arc<IrqData>) {}
|
||||
fn irq_eoi(&self, _irq_data: &Arc<IrqData>) {}
|
||||
|
||||
/// 指示当前芯片是否可以设置中断亲和性。
|
||||
fn can_set_affinity(&self) -> bool;
|
||||
@ -96,7 +96,7 @@ pub trait IrqChip: Sync + Send + Any + Debug {
|
||||
/// 不需要对提供的亲和性掩码进行完整性检查。这用于CPU热插拔,其中目标CPU尚未在cpu_online_mask中设置。
|
||||
fn irq_set_affinity(
|
||||
&self,
|
||||
_irq: &Arc<IrqData>,
|
||||
_irq_data: &Arc<IrqData>,
|
||||
_cpu: &CpuMask,
|
||||
_force: bool,
|
||||
) -> Result<IrqChipSetMaskResult, SystemError> {
|
||||
@ -104,7 +104,7 @@ pub trait IrqChip: Sync + Send + Any + Debug {
|
||||
}
|
||||
|
||||
/// retrigger an IRQ to the CPU
|
||||
fn retrigger(&self, _irq: &Arc<IrqData>) -> Result<(), SystemError> {
|
||||
fn retrigger(&self, _irq_data: &Arc<IrqData>) -> Result<(), SystemError> {
|
||||
Err(SystemError::ENOSYS)
|
||||
}
|
||||
|
||||
@ -119,65 +119,65 @@ pub trait IrqChip: Sync + Send + Any + Debug {
|
||||
///
|
||||
fn irq_set_type(
|
||||
&self,
|
||||
_irq: &Arc<IrqData>,
|
||||
_irq_data: &Arc<IrqData>,
|
||||
_flow_type: IrqLineStatus,
|
||||
) -> Result<IrqChipSetMaskResult, 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> {
|
||||
fn irq_set_wake(&self, _irq_data: &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> {
|
||||
fn irq_bus_lock(&self, _irq_data: &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> {
|
||||
fn irq_bus_sync_unlock(&self, _irq_data: &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>) {}
|
||||
fn irq_suspend(&self, _irq_data: &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>) {}
|
||||
fn irq_resume(&self, _irq_data: &Arc<IrqData>) {}
|
||||
|
||||
/// function called from core code on shutdown once per chip
|
||||
fn irq_pm_shutdown(&self, _irq: &Arc<IrqData>) {}
|
||||
fn irq_pm_shutdown(&self, _irq_data: &Arc<IrqData>) {}
|
||||
|
||||
/// Optional function to set irq_data.mask for special cases
|
||||
fn irq_calc_mask(&self, _irq: &Arc<IrqData>) {}
|
||||
fn irq_calc_mask(&self, _irq_data: &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> {
|
||||
fn irq_request_resources(&self, _irq_data: &Arc<IrqData>) -> Result<(), SystemError> {
|
||||
Ok(())
|
||||
}
|
||||
|
||||
/// optional to release resources acquired with
|
||||
/// irq_request_resources
|
||||
fn irq_release_resources(&self, _irq: &Arc<IrqData>) {}
|
||||
fn irq_release_resources(&self, _irq_data: &Arc<IrqData>) {}
|
||||
|
||||
/// optional to compose message content for MSI
|
||||
///
|
||||
/// 组装MSI消息并返回到msg中
|
||||
fn irq_compose_msi_msg(&self, _irq: &Arc<IrqData>, _msg: &mut MsiMsg) {}
|
||||
fn irq_compose_msi_msg(&self, _irq_data: &Arc<IrqData>, _msg: &mut MsiMsg) {}
|
||||
|
||||
/// optional to write message content for MSI
|
||||
fn irq_write_msi_msg(&self, _irq: &Arc<IrqData>, _msg: &MsiMsg) {}
|
||||
fn irq_write_msi_msg(&self, _irq_data: &Arc<IrqData>, _msg: &MsiMsg) {}
|
||||
|
||||
/// return the internal state of an interrupt
|
||||
fn irqchip_state(
|
||||
&self,
|
||||
_irq: &Arc<IrqData>,
|
||||
_irq_data: &Arc<IrqData>,
|
||||
_which: IrqChipState,
|
||||
) -> Result<bool, SystemError> {
|
||||
Err(SystemError::ENOSYS)
|
||||
@ -186,7 +186,7 @@ pub trait IrqChip: Sync + Send + Any + Debug {
|
||||
/// set the internal state of an interrupt
|
||||
fn set_irqchip_state(
|
||||
&self,
|
||||
_irq: &Arc<IrqData>,
|
||||
_irq_data: &Arc<IrqData>,
|
||||
_which: IrqChipState,
|
||||
_state: bool,
|
||||
) -> Result<(), SystemError> {
|
||||
@ -196,17 +196,17 @@ pub trait IrqChip: Sync + Send + Any + Debug {
|
||||
// todo: set vcpu affinity
|
||||
|
||||
/// send a single IPI to destination cpus
|
||||
fn send_single_ipi(&self, _irq: &Arc<IrqData>, _cpu: u32) {}
|
||||
fn send_single_ipi(&self, _irq_data: &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> {
|
||||
fn irq_nmi_setup(&self, _irq_data: &Arc<IrqData>) -> Result<(), SystemError> {
|
||||
Err(SystemError::ENOSYS)
|
||||
}
|
||||
|
||||
/// function called from core code after disabling an NMI
|
||||
fn irq_nmi_teardown(&self, _irq: &Arc<IrqData>) {}
|
||||
fn irq_nmi_teardown(&self, _irq_data: &Arc<IrqData>) {}
|
||||
|
||||
fn flags(&self) -> IrqChipFlags;
|
||||
}
|
||||
|
@ -80,6 +80,7 @@ impl IrqData {
|
||||
self.inner.lock_irqsave().desc = desc;
|
||||
}
|
||||
|
||||
#[allow(dead_code)]
|
||||
pub fn clear_irq_desc(&self) {
|
||||
self.inner.lock_irqsave().desc = Weak::new();
|
||||
}
|
||||
@ -210,6 +211,7 @@ impl IrqCommonData {
|
||||
handler_data: None,
|
||||
msi_desc: None,
|
||||
affinity: CpuMask::new(),
|
||||
effective_affinity: CpuMask::new(),
|
||||
};
|
||||
return IrqCommonData {
|
||||
inner: SpinLock::new(inner),
|
||||
@ -304,6 +306,10 @@ impl IrqCommonData {
|
||||
self.inner.lock_irqsave().affinity = affinity;
|
||||
}
|
||||
|
||||
pub fn set_effective_affinity(&self, affinity: CpuMask) {
|
||||
self.inner.lock_irqsave().effective_affinity = affinity;
|
||||
}
|
||||
|
||||
pub fn inner(&self) -> SpinLockGuard<InnerIrqCommonData> {
|
||||
self.inner.lock_irqsave()
|
||||
}
|
||||
@ -318,6 +324,7 @@ pub struct InnerIrqCommonData {
|
||||
handler_data: Option<Arc<dyn IrqHandlerData>>,
|
||||
msi_desc: Option<Arc<MsiDesc>>,
|
||||
affinity: CpuMask,
|
||||
effective_affinity: CpuMask,
|
||||
}
|
||||
|
||||
impl InnerIrqCommonData {
|
||||
@ -338,6 +345,10 @@ impl InnerIrqCommonData {
|
||||
pub fn handler_data(&self) -> Option<Arc<dyn IrqHandlerData>> {
|
||||
self.handler_data.clone()
|
||||
}
|
||||
|
||||
pub fn effective_affinity(&self) -> &CpuMask {
|
||||
&self.effective_affinity
|
||||
}
|
||||
}
|
||||
|
||||
/// 中断处理函数传入的数据
|
||||
|
@ -114,6 +114,7 @@ impl IrqDesc {
|
||||
kern_inode: None,
|
||||
kset: None,
|
||||
parent_kobj: None,
|
||||
threads_oneshot: 0,
|
||||
}),
|
||||
request_mutex: Mutex::new(()),
|
||||
handler: RwLock::new(None),
|
||||
@ -285,6 +286,14 @@ impl IrqDesc {
|
||||
);
|
||||
}
|
||||
|
||||
pub fn set_probe(&self) {
|
||||
self.modify_status(IrqLineStatus::IRQ_NOPROBE, IrqLineStatus::empty());
|
||||
}
|
||||
|
||||
pub fn set_noprobe(&self) {
|
||||
self.modify_status(IrqLineStatus::empty(), IrqLineStatus::IRQ_NOPROBE);
|
||||
}
|
||||
|
||||
pub fn modify_status(&self, clear: IrqLineStatus, set: IrqLineStatus) {
|
||||
let mut desc_guard = self.inner();
|
||||
desc_guard.line_status.remove(clear);
|
||||
@ -365,6 +374,8 @@ pub struct InnerIrqDesc {
|
||||
/// per-cpu affinity
|
||||
percpu_affinity: Option<CpuMask>,
|
||||
// wait_for_threads: EventWaitQueue
|
||||
/// bitfield to handle shared oneshot threads
|
||||
threads_oneshot: u64,
|
||||
}
|
||||
|
||||
impl InnerIrqDesc {
|
||||
@ -444,6 +455,10 @@ impl InnerIrqDesc {
|
||||
!self.line_status.contains(IrqLineStatus::IRQ_NOTHREAD)
|
||||
}
|
||||
|
||||
pub fn threads_oneshot(&self) -> u64 {
|
||||
self.threads_oneshot
|
||||
}
|
||||
|
||||
/// 中断是否可以设置CPU亲和性
|
||||
pub fn can_set_affinity(&self) -> bool {
|
||||
if !self.common_data.status().can_balance()
|
||||
|
@ -110,8 +110,6 @@ impl IrqDomainManager {
|
||||
|
||||
self.add_domain(domain.clone());
|
||||
|
||||
self.domain_associate_many(&domain, first_irq, first_hwirq, irq_size);
|
||||
|
||||
return Some(domain);
|
||||
}
|
||||
|
||||
@ -248,6 +246,10 @@ impl IrqDomainManager {
|
||||
/// 这是调用 domain_ops->activate 以编程中断控制器的第二步,以便中断实际上可以被传递。
|
||||
pub fn activate_irq(&self, irq_data: &Arc<IrqData>, reserve: bool) -> Result<(), SystemError> {
|
||||
let mut r = Ok(());
|
||||
// kdebug!(
|
||||
// "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);
|
||||
}
|
||||
@ -268,7 +270,9 @@ impl IrqDomainManager {
|
||||
let mut r = Ok(());
|
||||
|
||||
if let Some(irq_data) = irq_data {
|
||||
// kdebug!("do_activate_irq: irq_data={:?}", irq_data);
|
||||
if let Some(domain) = irq_data.domain() {
|
||||
// kdebug!("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);
|
||||
|
@ -155,7 +155,7 @@ impl IrqManager {
|
||||
*action_guard.flags_mut() = flags;
|
||||
*action_guard.dev_id_mut() = dev_id;
|
||||
drop(action_guard);
|
||||
|
||||
kdebug!("to inner_setup_irq");
|
||||
return self.inner_setup_irq(irq, irqaction, desc);
|
||||
}
|
||||
|
||||
@ -370,6 +370,15 @@ impl IrqManager {
|
||||
|| ((old_guard.flags().bitxor(*action_guard.flags()))
|
||||
.contains(IrqHandleFlags::IRQF_ONESHOT))
|
||||
{
|
||||
kdebug!(
|
||||
"Flags mismatch for irq {} (name: {}, flags: {:?}). old action name: {}, old flags: {:?}",
|
||||
irq.data(),
|
||||
action_guard.name(),
|
||||
action_guard.flags(),
|
||||
old_guard.name(),
|
||||
old_guard.flags()
|
||||
);
|
||||
|
||||
return Err(err_out_mismatch(
|
||||
old_guard,
|
||||
desc_inner_guard,
|
||||
@ -383,6 +392,12 @@ impl IrqManager {
|
||||
if *old_guard.flags() & IrqHandleFlags::IRQF_PERCPU
|
||||
!= *action_guard.flags() & IrqHandleFlags::IRQF_PERCPU
|
||||
{
|
||||
kdebug!(
|
||||
"Per-cpu mismatch for irq {} (name: {}, flags: {:?})",
|
||||
irq.data(),
|
||||
action_guard.name(),
|
||||
action_guard.flags()
|
||||
);
|
||||
return Err(err_out_mismatch(
|
||||
old_guard,
|
||||
desc_inner_guard,
|
||||
@ -436,6 +451,13 @@ impl IrqManager {
|
||||
if let Err(e) =
|
||||
self.do_set_irq_trigger(desc.clone(), &mut desc_inner_guard, trigger_type)
|
||||
{
|
||||
kdebug!(
|
||||
"Failed to set trigger type for irq {} (name: {}, flags: {:?}), error {:?}",
|
||||
irq.data(),
|
||||
action_guard.name(),
|
||||
action_guard.flags(),
|
||||
e
|
||||
);
|
||||
return Err(err_out_unlock(
|
||||
e,
|
||||
desc_inner_guard,
|
||||
@ -445,9 +467,16 @@ impl IrqManager {
|
||||
));
|
||||
}
|
||||
}
|
||||
|
||||
kdebug!("to irq_activate");
|
||||
// 激活中断。这种激活必须独立于IRQ_NOAUTOEN进行*desc_inner_guard.internal_state_mut() |= IrqDescState::IRQS_NOREQUEST;uest.
|
||||
if let Err(e) = self.irq_activate(&desc, &mut desc_inner_guard) {
|
||||
kdebug!(
|
||||
"Failed to activate irq {} (name: {}, flags: {:?}), error {:?}",
|
||||
irq.data(),
|
||||
action_guard.name(),
|
||||
action_guard.flags(),
|
||||
e
|
||||
);
|
||||
return Err(err_out_unlock(
|
||||
e,
|
||||
desc_inner_guard,
|
||||
@ -599,6 +628,11 @@ impl IrqManager {
|
||||
desc_inner_guard: &mut SpinLockGuard<'_, InnerIrqDesc>,
|
||||
resend: bool,
|
||||
) -> Result<(), SystemError> {
|
||||
kdebug!(
|
||||
"irq_activate_and_startup: irq: {}, name: {:?}",
|
||||
desc.irq().data(),
|
||||
desc_inner_guard.name()
|
||||
);
|
||||
self.irq_activate(desc, desc_inner_guard)?;
|
||||
self.irq_startup(desc, desc_inner_guard, resend, Self::IRQ_START_FORCE)
|
||||
}
|
||||
@ -625,6 +659,11 @@ impl IrqManager {
|
||||
resend: bool,
|
||||
force: bool,
|
||||
) -> Result<(), SystemError> {
|
||||
kdebug!(
|
||||
"irq_startup: irq: {}, name: {:?}",
|
||||
desc_inner_guard.irq_data().irq().data(),
|
||||
desc_inner_guard.name()
|
||||
);
|
||||
let mut ret = Ok(());
|
||||
let irq_data = desc_inner_guard.irq_data().clone();
|
||||
let affinity = desc_inner_guard.common_data().affinity();
|
||||
@ -745,7 +784,15 @@ impl IrqManager {
|
||||
);
|
||||
}
|
||||
|
||||
pub fn irq_do_set_affinity(
|
||||
pub fn irq_set_affinity(
|
||||
&self,
|
||||
irq_data: &Arc<IrqData>,
|
||||
desc_inner_guard: &SpinLockGuard<'_, InnerIrqDesc>,
|
||||
cpumask: &CpuMask,
|
||||
) -> Result<(), SystemError> {
|
||||
return self.irq_do_set_affinity(irq_data, desc_inner_guard, cpumask, false);
|
||||
}
|
||||
fn irq_do_set_affinity(
|
||||
&self,
|
||||
irq_data: &Arc<IrqData>,
|
||||
desc_inner_guard: &SpinLockGuard<'_, InnerIrqDesc>,
|
||||
@ -1050,6 +1097,8 @@ impl IrqManager {
|
||||
/// ## 注意
|
||||
///
|
||||
/// 此函数不可以在中断上下文中调用。
|
||||
///
|
||||
/// 参考 https://code.dragonos.org.cn/xref/linux-6.6.21/kernel/irq/manage.c#2026
|
||||
pub fn free_irq(&self, _irq: IrqNumber, _dev_id: Option<Arc<DeviceId>>) {
|
||||
kwarn!("Unimplemented free_irq");
|
||||
}
|
||||
|
@ -16,6 +16,21 @@ impl CpuMask {
|
||||
Self { bmp }
|
||||
}
|
||||
|
||||
/// # from_cpu - 从指定的CPU创建CPU掩码
|
||||
///
|
||||
/// 该函数用于根据给定的CPU标识创建一个CPU掩码,只有指定的CPU被设置为激活状态。
|
||||
///
|
||||
/// ## 参数
|
||||
/// - `cpu`: `ProcessorId`,指定要设置为激活状态的CPU。
|
||||
///
|
||||
/// ## 返回值
|
||||
/// - `Self`: 返回一个新的`CpuMask`实例,其中只有指定的CPU被设置为激活状态。
|
||||
pub fn from_cpu(cpu: ProcessorId) -> Self {
|
||||
let mut mask = Self::new();
|
||||
mask.set(cpu, true);
|
||||
mask
|
||||
}
|
||||
|
||||
/// 获取CpuMask中的第一个cpu
|
||||
pub fn first(&self) -> Option<ProcessorId> {
|
||||
self.bmp
|
||||
@ -86,14 +101,24 @@ impl CpuMask {
|
||||
pub fn inner(&self) -> &AllocBitmap {
|
||||
&self.bmp
|
||||
}
|
||||
|
||||
pub fn bitand_assign(&mut self, rhs: &CpuMask) {
|
||||
self.bmp.bitand_assign(&rhs.bmp);
|
||||
}
|
||||
}
|
||||
|
||||
impl BitAnd for CpuMask {
|
||||
type Output = Self;
|
||||
impl BitAnd for &CpuMask {
|
||||
type Output = CpuMask;
|
||||
|
||||
fn bitand(self, rhs: Self) -> Self::Output {
|
||||
let bmp = self.bmp & rhs.bmp;
|
||||
Self { bmp }
|
||||
fn bitand(self, rhs: &CpuMask) -> Self::Output {
|
||||
let bmp = &self.bmp & &rhs.bmp;
|
||||
CpuMask { bmp }
|
||||
}
|
||||
}
|
||||
|
||||
impl Default for CpuMask {
|
||||
fn default() -> Self {
|
||||
Self::new()
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -1,3 +1,4 @@
|
||||
use crate::libs::align::{page_align_down, page_align_up};
|
||||
use crate::libs::spinlock::{SpinLock, SpinLockGuard};
|
||||
use crate::mm::kernel_mapper::KernelMapper;
|
||||
use crate::mm::page::{PAGE_1G_SHIFT, PAGE_4K_SHIFT};
|
||||
@ -682,6 +683,41 @@ impl MMIOSpaceGuard {
|
||||
return r;
|
||||
}
|
||||
|
||||
/// # map_any_phys - 将任意物理地址映射到虚拟地址
|
||||
///
|
||||
/// 将指定的物理地址和长度映射到虚拟地址空间。
|
||||
///
|
||||
/// ## 参数
|
||||
///
|
||||
/// - `paddr`: 物理地址,需要被映射的起始地址。
|
||||
/// - `length`: 要映射的物理地址长度。
|
||||
///
|
||||
/// ## 返回值
|
||||
/// - `Ok(VirtAddr)`: 映射成功,返回虚拟地址的起始地址。
|
||||
/// - `Err(SystemError)`: 映射失败,返回系统错误。
|
||||
///
|
||||
/// ## 副作用
|
||||
///
|
||||
/// 该函数会修改虚拟地址空间,将物理地址映射到虚拟地址。
|
||||
///
|
||||
/// ## Safety
|
||||
///
|
||||
/// 由于该函数涉及到内存操作,因此它是非安全的。确保在调用该函数时,你传入的物理地址是正确的。
|
||||
#[allow(dead_code)]
|
||||
pub unsafe fn map_any_phys(
|
||||
&self,
|
||||
paddr: PhysAddr,
|
||||
length: usize,
|
||||
) -> Result<VirtAddr, SystemError> {
|
||||
let paddr_base = PhysAddr::new(page_align_down(paddr.data()));
|
||||
let offset = paddr - paddr_base;
|
||||
let vaddr_base = self.vaddr;
|
||||
let vaddr = vaddr_base + offset;
|
||||
|
||||
self.map_phys(paddr_base, page_align_up(length + offset))?;
|
||||
return Ok(vaddr);
|
||||
}
|
||||
|
||||
/// 泄露一个MMIO space guard,不会释放映射的空间
|
||||
pub unsafe fn leak(self) {
|
||||
core::mem::forget(self);
|
||||
|
@ -64,6 +64,7 @@ impl CpuHpCpuState {
|
||||
}
|
||||
}
|
||||
|
||||
#[allow(dead_code)]
|
||||
pub const fn thread(&self) -> &Option<Arc<ProcessControlBlock>> {
|
||||
&self.thread
|
||||
}
|
||||
@ -305,6 +306,7 @@ pub fn smp_cpu_manager_init(boot_cpu: ProcessorId) {
|
||||
}
|
||||
|
||||
unsafe { smp_cpu_manager().set_possible_cpu(boot_cpu, true) };
|
||||
unsafe { smp_cpu_manager().set_present_cpu(boot_cpu, true) };
|
||||
|
||||
SmpCpuManager::arch_init(boot_cpu);
|
||||
}
|
||||
|
Loading…
x
Reference in New Issue
Block a user