diff --git a/kernel/comps/keyboard/src/i8042_chip/keyboard.rs b/kernel/comps/keyboard/src/i8042_chip/keyboard.rs index 7906d77f4..239b58d7d 100644 --- a/kernel/comps/keyboard/src/i8042_chip/keyboard.rs +++ b/kernel/comps/keyboard/src/i8042_chip/keyboard.rs @@ -7,7 +7,7 @@ use core::sync::atomic::{AtomicBool, Ordering}; use aster_input::key::KeyStatus; use ostd::{ arch::{ - kernel::{MappedIrqLine, IRQ_CHIP}, + irq::{MappedIrqLine, IRQ_CHIP}, trap::TrapFrame, }, irq::IrqLine, diff --git a/kernel/comps/virtio/src/transport/mmio/bus/common_device.rs b/kernel/comps/virtio/src/transport/mmio/bus/common_device.rs index 732ad146f..53c057ac8 100644 --- a/kernel/comps/virtio/src/transport/mmio/bus/common_device.rs +++ b/kernel/comps/virtio/src/transport/mmio/bus/common_device.rs @@ -10,7 +10,7 @@ use int_to_c_enum::TryFromInt; use log::info; #[cfg(target_arch = "x86_64")] -use ostd::arch::kernel::MappedIrqLine; +use ostd::arch::irq::MappedIrqLine; #[cfg(target_arch = "riscv64")] // TODO: Add `MappedIrqLine` support for RISC-V. use ostd::irq::IrqLine as MappedIrqLine; #[cfg(target_arch = "loongarch64")] // TODO: Add `MappedIrqLine` support for Loongarch. diff --git a/kernel/comps/virtio/src/transport/mmio/bus/mod.rs b/kernel/comps/virtio/src/transport/mmio/bus/mod.rs index d2f721b19..5788436c2 100644 --- a/kernel/comps/virtio/src/transport/mmio/bus/mod.rs +++ b/kernel/comps/virtio/src/transport/mmio/bus/mod.rs @@ -28,7 +28,7 @@ pub(super) fn init() { fn x86_probe() { use common_device::{mmio_check_magic, mmio_read_device_id, MmioCommonDevice}; use log::debug; - use ostd::{arch::kernel::IRQ_CHIP, io::IoMem, irq::IrqLine}; + use ostd::{arch::irq::IRQ_CHIP, io::IoMem, irq::IrqLine}; // TODO: The correct method for detecting VirtIO-MMIO devices on x86_64 systems is to parse the // kernel command line if ACPI tables are absent [1], or the ACPI SSDT if ACPI tables are diff --git a/ostd/src/arch/loongarch/cpu/context.rs b/ostd/src/arch/loongarch/cpu/context.rs index 331c61d02..12a76d2ff 100644 --- a/ostd/src/arch/loongarch/cpu/context.rs +++ b/ostd/src/arch/loongarch/cpu/context.rs @@ -218,7 +218,7 @@ impl UserContextApiInternal for UserContext { | Interrupt::HWI6 | Interrupt::HWI7 => { log::debug!("Handling hardware interrupt: {:?}", interrupt); - while let Some(irq) = crate::arch::kernel::irq::claim() { + while let Some(irq) = crate::arch::irq::chip::claim() { // Call the IRQ callback functions for the claimed interrupt call_irq_callback_functions(&self.as_trap_frame(), irq as _, PrivilegeLevel::User); } diff --git a/ostd/src/arch/loongarch/irq.rs b/ostd/src/arch/loongarch/irq.rs deleted file mode 100644 index e9abf7897..000000000 --- a/ostd/src/arch/loongarch/irq.rs +++ /dev/null @@ -1,84 +0,0 @@ -// SPDX-License-Identifier: MPL-2.0 - -//! Interrupts. - -use crate::cpu::PinCurrentCpu; - -pub(crate) const IRQ_NUM_MIN: u8 = 32; -pub(crate) const IRQ_NUM_MAX: u8 = 255; - -pub(crate) struct IrqRemapping { - _private: (), -} - -impl IrqRemapping { - pub(crate) const fn new() -> Self { - Self { _private: () } - } - - /// Initializes the remapping entry for the specific IRQ number. - /// - /// This will do nothing if the entry is already initialized or interrupt - /// remapping is disabled or not supported by the architecture. - pub(crate) fn init(&self, _irq_num: u8) {} - - /// Gets the remapping index of the IRQ line. - /// - /// This method will return `None` if interrupt remapping is disabled or - /// not supported by the architecture. - pub(crate) fn remapping_index(&self) -> Option { - None - } -} - -pub(crate) fn enable_local() { - loongArch64::register::crmd::set_ie(true); -} - -/// Enables local IRQs and halts the CPU to wait for interrupts. -/// -/// This method guarantees that no interrupts can occur in the middle. In other words, IRQs must -/// either have been processed before this method is called, or they must wake the CPU up from the -/// halting state. -// -// FIXME: Mark this as unsafe. See -// . -pub(crate) fn enable_local_and_halt() { - loongArch64::register::crmd::set_ie(true); - // TODO: We should put the CPU into the idle state. However, doing so - // without creating race conditions (see the doc comments above) in - // LoongArch is challenging. Therefore, we now simply return here, as - // spurious wakeups are acceptable for this method. -} - -pub(crate) fn disable_local() { - loongArch64::register::crmd::set_ie(false); -} - -pub(crate) fn is_local_enabled() -> bool { - loongArch64::register::crmd::read().ie() -} - -// ####### Inter-Processor Interrupts (IPIs) ####### - -/// Hardware-specific, architecture-dependent CPU ID. -#[derive(Debug, Clone, Copy, PartialEq, Eq)] -pub(crate) struct HwCpuId(u32); - -impl HwCpuId { - pub(crate) fn read_current(_guard: &dyn PinCurrentCpu) -> Self { - // TODO: Support SMP in LoongArch. - Self(0) - } -} - -/// Sends a general inter-processor interrupt (IPI) to the specified CPU. -/// -/// # Safety -/// -/// The caller must ensure that the interrupt number is valid and that -/// the corresponding handler is configured correctly on the remote CPU. -/// Furthermore, invoking the interrupt handler must also be safe. -pub(crate) unsafe fn send_ipi(_hw_cpu_id: HwCpuId, _irq_num: u8, _guard: &dyn PinCurrentCpu) { - unimplemented!() -} diff --git a/ostd/src/arch/loongarch/kernel/irq/eiointc.rs b/ostd/src/arch/loongarch/irq/chip/eiointc.rs similarity index 100% rename from ostd/src/arch/loongarch/kernel/irq/eiointc.rs rename to ostd/src/arch/loongarch/irq/chip/eiointc.rs diff --git a/ostd/src/arch/loongarch/kernel/irq/mod.rs b/ostd/src/arch/loongarch/irq/chip/mod.rs similarity index 93% rename from ostd/src/arch/loongarch/kernel/irq/mod.rs rename to ostd/src/arch/loongarch/irq/chip/mod.rs index 395b8db49..6c1f62b89 100644 --- a/ostd/src/arch/loongarch/kernel/irq/mod.rs +++ b/ostd/src/arch/loongarch/irq/chip/mod.rs @@ -4,7 +4,8 @@ mod eiointc; use loongArch64::register::ecfg::LineBasedInterrupt; -use crate::arch::{irq, kernel::irq::eiointc::Eiointc}; +use self::eiointc::Eiointc; +use crate::arch::irq; pub(in crate::arch) fn init() { // FIXME: Support SMP in LoongArch diff --git a/ostd/src/arch/loongarch/irq/ipi.rs b/ostd/src/arch/loongarch/irq/ipi.rs new file mode 100644 index 000000000..68115ce96 --- /dev/null +++ b/ostd/src/arch/loongarch/irq/ipi.rs @@ -0,0 +1,27 @@ +// SPDX-License-Identifier: MPL-2.0 + +//! Inter-processor interrupts. + +use crate::cpu::PinCurrentCpu; + +/// Hardware-specific, architecture-dependent CPU ID. +#[derive(Debug, Clone, Copy, PartialEq, Eq)] +pub(crate) struct HwCpuId(u32); + +impl HwCpuId { + pub(crate) fn read_current(_guard: &dyn PinCurrentCpu) -> Self { + // TODO: Support SMP in LoongArch. + Self(0) + } +} + +/// Sends a general inter-processor interrupt (IPI) to the specified CPU. +/// +/// # Safety +/// +/// The caller must ensure that the interrupt number is valid and that +/// the corresponding handler is configured correctly on the remote CPU. +/// Furthermore, invoking the interrupt handler must also be safe. +pub(crate) unsafe fn send_ipi(_hw_cpu_id: HwCpuId, _irq_num: u8, _guard: &dyn PinCurrentCpu) { + unimplemented!() +} diff --git a/ostd/src/arch/loongarch/irq/mod.rs b/ostd/src/arch/loongarch/irq/mod.rs new file mode 100644 index 000000000..246513c39 --- /dev/null +++ b/ostd/src/arch/loongarch/irq/mod.rs @@ -0,0 +1,15 @@ +// SPDX-License-Identifier: MPL-2.0 + +//! Interrupts. + +pub(super) mod chip; +mod ipi; +mod ops; +mod remapping; + +pub(crate) use ipi::{send_ipi, HwCpuId}; +pub(crate) use ops::{disable_local, enable_local, enable_local_and_halt, is_local_enabled}; +pub(crate) use remapping::IrqRemapping; + +pub(crate) const IRQ_NUM_MIN: u8 = 0; +pub(crate) const IRQ_NUM_MAX: u8 = 255; diff --git a/ostd/src/arch/loongarch/irq/ops.rs b/ostd/src/arch/loongarch/irq/ops.rs new file mode 100644 index 000000000..307bf55a6 --- /dev/null +++ b/ostd/src/arch/loongarch/irq/ops.rs @@ -0,0 +1,33 @@ +// SPDX-License-Identifier: MPL-2.0 + +//! Interrupt operations. + +// FIXME: Mark this as unsafe. See +// . +pub(crate) fn enable_local() { + loongArch64::register::crmd::set_ie(true); +} + +/// Enables local IRQs and halts the CPU to wait for interrupts. +/// +/// This method guarantees that no interrupts can occur in the middle. In other words, IRQs must +/// either have been processed before this method is called, or they must wake the CPU up from the +/// halting state. +// +// FIXME: Mark this as unsafe. See +// . +pub(crate) fn enable_local_and_halt() { + loongArch64::register::crmd::set_ie(true); + // TODO: We should put the CPU into the idle state. However, doing so + // without creating race conditions (see the doc comments above) in + // LoongArch is challenging. Therefore, we now simply return here, as + // spurious wakeups are acceptable for this method. +} + +pub(crate) fn disable_local() { + loongArch64::register::crmd::set_ie(false); +} + +pub(crate) fn is_local_enabled() -> bool { + loongArch64::register::crmd::read().ie() +} diff --git a/ostd/src/arch/loongarch/irq/remapping.rs b/ostd/src/arch/loongarch/irq/remapping.rs new file mode 100644 index 000000000..18ca69c1a --- /dev/null +++ b/ostd/src/arch/loongarch/irq/remapping.rs @@ -0,0 +1,25 @@ +// SPDX-License-Identifier: MPL-2.0 + +pub(crate) struct IrqRemapping { + _private: (), +} + +impl IrqRemapping { + pub(crate) const fn new() -> Self { + Self { _private: () } + } + + /// Initializes the remapping entry for the specific IRQ number. + /// + /// This will do nothing if the entry is already initialized or interrupt + /// remapping is disabled or not supported by the architecture. + pub(crate) fn init(&self, _irq_num: u8) {} + + /// Gets the remapping index of the IRQ line. + /// + /// This method will return `None` if interrupt remapping is disabled or + /// not supported by the architecture. + pub(crate) fn remapping_index(&self) -> Option { + None + } +} diff --git a/ostd/src/arch/loongarch/kernel/mod.rs b/ostd/src/arch/loongarch/kernel/mod.rs deleted file mode 100644 index d19ce4cc1..000000000 --- a/ostd/src/arch/loongarch/kernel/mod.rs +++ /dev/null @@ -1,8 +0,0 @@ -// SPDX-License-Identifier: MPL-2.0 - -//! Architecture kernel module. -// -// TODO: The purpose of this module is too ambiguous. We should split it up and move its submodules -// to more suitable locations. - -pub(super) mod irq; diff --git a/ostd/src/arch/loongarch/mod.rs b/ostd/src/arch/loongarch/mod.rs index 4c36eaf48..e23bcd5d6 100644 --- a/ostd/src/arch/loongarch/mod.rs +++ b/ostd/src/arch/loongarch/mod.rs @@ -10,7 +10,6 @@ pub mod device; mod io; pub(crate) mod iommu; pub(crate) mod irq; -pub mod kernel; pub(crate) mod mm; pub mod qemu; pub(crate) mod serial; @@ -29,7 +28,7 @@ pub(crate) unsafe fn late_init_on_bsp() { let io_mem_builder = io::construct_io_mem_allocator_builder(); - kernel::irq::init(); + irq::chip::init(); // SAFETY: We're on the BSP and we're ready to boot all APs. unsafe { crate::boot::smp::boot_all_aps() }; @@ -45,7 +44,7 @@ pub(crate) unsafe fn init_on_ap() { } pub(crate) fn interrupts_ack(irq_number: usize) { - kernel::irq::complete(irq_number as _); + irq::chip::complete(irq_number as _); } /// Returns the frequency of TSC. The unit is Hz. diff --git a/ostd/src/arch/loongarch/trap/mod.rs b/ostd/src/arch/loongarch/trap/mod.rs index 1f446440c..b47523051 100644 --- a/ostd/src/arch/loongarch/trap/mod.rs +++ b/ostd/src/arch/loongarch/trap/mod.rs @@ -86,7 +86,7 @@ extern "C" fn trap_handler(f: &mut TrapFrame) { | Interrupt::HWI6 | Interrupt::HWI7 => { log::debug!("Handling hardware interrupt: {:?}", interrupt); - while let Some(irq) = crate::arch::kernel::irq::claim() { + while let Some(irq) = crate::arch::irq::chip::claim() { // Call the IRQ callback functions for the claimed interrupt call_irq_callback_functions(f, irq as _, PrivilegeLevel::Kernel); } diff --git a/ostd/src/arch/riscv/irq.rs b/ostd/src/arch/riscv/irq.rs deleted file mode 100644 index 21bf07be6..000000000 --- a/ostd/src/arch/riscv/irq.rs +++ /dev/null @@ -1,92 +0,0 @@ -// SPDX-License-Identifier: MPL-2.0 - -//! Interrupts. - -use crate::cpu::PinCurrentCpu; - -pub(crate) const IRQ_NUM_MIN: u8 = 0; -pub(crate) const IRQ_NUM_MAX: u8 = 255; - -pub(crate) struct IrqRemapping { - _private: (), -} - -impl IrqRemapping { - pub(crate) const fn new() -> Self { - Self { _private: () } - } - - /// Initializes the remapping entry for the specific IRQ number. - /// - /// This will do nothing if the entry is already initialized or interrupt - /// remapping is disabled or not supported by the architecture. - pub(crate) fn init(&self, _irq_num: u8) {} - - /// Gets the remapping index of the IRQ line. - /// - /// This method will return `None` if interrupt remapping is disabled or - /// not supported by the architecture. - pub(crate) fn remapping_index(&self) -> Option { - None - } -} - -// FIXME: Mark this as unsafe. See -// . -pub(crate) fn enable_local() { - // SAFETY: The safety is upheld by the caller. - unsafe { riscv::interrupt::enable() } -} - -/// Enables local IRQs and halts the CPU to wait for interrupts. -/// -/// This method guarantees that no interrupts can occur in the middle. In other words, IRQs must -/// either have been processed before this method is called, or they must wake the CPU up from the -/// halting state. -// -// FIXME: Mark this as unsafe. See -// . -pub(crate) fn enable_local_and_halt() { - // RISC-V Instruction Set Manual, Machine-Level ISA, Version 1.13 says: - // "The WFI instruction can also be executed when interrupts are disabled. The operation of WFI - // must be unaffected by the global interrupt bits in `mstatus` (MIE and SIE) [..]" - // - // So we can use `wfi` even if IRQs are disabled. Pending IRQs can still wake up the CPU, but - // they will only occur later when we enable local IRQs. - riscv::asm::wfi(); - - // SAFETY: The safety is upheld by the caller. - unsafe { riscv::interrupt::enable() } -} - -pub(crate) fn disable_local() { - riscv::interrupt::disable(); -} - -pub(crate) fn is_local_enabled() -> bool { - riscv::register::sstatus::read().sie() -} - -// ####### Inter-Processor Interrupts (IPIs) ####### - -/// Hardware-specific, architecture-dependent CPU ID. -#[derive(Debug, Clone, Copy, PartialEq, Eq)] -pub(crate) struct HwCpuId(u32); - -impl HwCpuId { - pub(crate) fn read_current(_guard: &dyn PinCurrentCpu) -> Self { - // TODO: Support SMP in RISC-V. - Self(0) - } -} - -/// Sends a general inter-processor interrupt (IPI) to the specified CPU. -/// -/// # Safety -/// -/// The caller must ensure that the interrupt number is valid and that -/// the corresponding handler is configured correctly on the remote CPU. -/// Furthermore, invoking the interrupt handler must also be safe. -pub(crate) unsafe fn send_ipi(_hw_cpu_id: HwCpuId, _irq_num: u8, _guard: &dyn PinCurrentCpu) { - unimplemented!() -} diff --git a/ostd/src/arch/riscv/irq/ipi.rs b/ostd/src/arch/riscv/irq/ipi.rs new file mode 100644 index 000000000..e7f1c2f42 --- /dev/null +++ b/ostd/src/arch/riscv/irq/ipi.rs @@ -0,0 +1,27 @@ +// SPDX-License-Identifier: MPL-2.0 + +//! Inter-processor interrupts. + +use crate::cpu::PinCurrentCpu; + +/// Hardware-specific, architecture-dependent CPU ID. +#[derive(Debug, Clone, Copy, PartialEq, Eq)] +pub(crate) struct HwCpuId(u32); + +impl HwCpuId { + pub(crate) fn read_current(_guard: &dyn PinCurrentCpu) -> Self { + // TODO: Support SMP in RISC-V. + Self(0) + } +} + +/// Sends a general inter-processor interrupt (IPI) to the specified CPU. +/// +/// # Safety +/// +/// The caller must ensure that the interrupt number is valid and that +/// the corresponding handler is configured correctly on the remote CPU. +/// Furthermore, invoking the interrupt handler must also be safe. +pub(crate) unsafe fn send_ipi(_hw_cpu_id: HwCpuId, _irq_num: u8, _guard: &dyn PinCurrentCpu) { + unimplemented!() +} diff --git a/ostd/src/arch/riscv/irq/mod.rs b/ostd/src/arch/riscv/irq/mod.rs new file mode 100644 index 000000000..b510d4aac --- /dev/null +++ b/ostd/src/arch/riscv/irq/mod.rs @@ -0,0 +1,14 @@ +// SPDX-License-Identifier: MPL-2.0 + +//! Interrupts. + +mod ipi; +mod ops; +mod remapping; + +pub(crate) use ipi::{send_ipi, HwCpuId}; +pub(crate) use ops::{disable_local, enable_local, enable_local_and_halt, is_local_enabled}; +pub(crate) use remapping::IrqRemapping; + +pub(crate) const IRQ_NUM_MIN: u8 = 0; +pub(crate) const IRQ_NUM_MAX: u8 = 255; diff --git a/ostd/src/arch/riscv/irq/ops.rs b/ostd/src/arch/riscv/irq/ops.rs new file mode 100644 index 000000000..6760e90e9 --- /dev/null +++ b/ostd/src/arch/riscv/irq/ops.rs @@ -0,0 +1,39 @@ +// SPDX-License-Identifier: MPL-2.0 + +//! Interrupt operations. + +// FIXME: Mark this as unsafe. See +// . +pub(crate) fn enable_local() { + // SAFETY: The safety is upheld by the caller. + unsafe { riscv::interrupt::enable() } +} + +/// Enables local IRQs and halts the CPU to wait for interrupts. +/// +/// This method guarantees that no interrupts can occur in the middle. In other words, IRQs must +/// either have been processed before this method is called, or they must wake the CPU up from the +/// halting state. +// +// FIXME: Mark this as unsafe. See +// . +pub(crate) fn enable_local_and_halt() { + // RISC-V Instruction Set Manual, Machine-Level ISA, Version 1.13 says: + // "The WFI instruction can also be executed when interrupts are disabled. The operation of WFI + // must be unaffected by the global interrupt bits in `mstatus` (MIE and SIE) [..]" + // + // So we can use `wfi` even if IRQs are disabled. Pending IRQs can still wake up the CPU, but + // they will only occur later when we enable local IRQs. + riscv::asm::wfi(); + + // SAFETY: The safety is upheld by the caller. + unsafe { riscv::interrupt::enable() } +} + +pub(crate) fn disable_local() { + riscv::interrupt::disable(); +} + +pub(crate) fn is_local_enabled() -> bool { + riscv::register::sstatus::read().sie() +} diff --git a/ostd/src/arch/riscv/irq/remapping.rs b/ostd/src/arch/riscv/irq/remapping.rs new file mode 100644 index 000000000..18ca69c1a --- /dev/null +++ b/ostd/src/arch/riscv/irq/remapping.rs @@ -0,0 +1,25 @@ +// SPDX-License-Identifier: MPL-2.0 + +pub(crate) struct IrqRemapping { + _private: (), +} + +impl IrqRemapping { + pub(crate) const fn new() -> Self { + Self { _private: () } + } + + /// Initializes the remapping entry for the specific IRQ number. + /// + /// This will do nothing if the entry is already initialized or interrupt + /// remapping is disabled or not supported by the architecture. + pub(crate) fn init(&self, _irq_num: u8) {} + + /// Gets the remapping index of the IRQ line. + /// + /// This method will return `None` if interrupt remapping is disabled or + /// not supported by the architecture. + pub(crate) fn remapping_index(&self) -> Option { + None + } +} diff --git a/ostd/src/arch/x86/irq.rs b/ostd/src/arch/x86/irq.rs deleted file mode 100644 index b0321f857..000000000 --- a/ostd/src/arch/x86/irq.rs +++ /dev/null @@ -1,136 +0,0 @@ -// SPDX-License-Identifier: MPL-2.0 - -//! Interrupts. - -use spin::Once; -use x86_64::registers::rflags::{self, RFlags}; - -use super::iommu::{alloc_irt_entry, has_interrupt_remapping, IrtEntryHandle}; -use crate::cpu::PinCurrentCpu; - -// Intel(R) 64 and IA-32 rchitectures Software Developer's Manual, -// Volume 3A, Section 6.2 says "Vector numbers in the range 32 to 255 -// are designated as user-defined interrupts and are not reserved by -// the Intel 64 and IA-32 architecture." -pub(crate) const IRQ_NUM_MIN: u8 = 32; -pub(crate) const IRQ_NUM_MAX: u8 = 255; - -pub(crate) struct IrqRemapping { - entry: Once, -} - -impl IrqRemapping { - pub(crate) const fn new() -> Self { - Self { entry: Once::new() } - } - - /// Initializes the remapping entry for the specific IRQ number. - /// - /// This will do nothing if the entry is already initialized or interrupt - /// remapping is disabled or not supported by the architecture. - pub(crate) fn init(&self, irq_num: u8) { - if !has_interrupt_remapping() { - return; - } - - self.entry.call_once(|| { - // Allocate and enable the IRT entry. - let handle = alloc_irt_entry().unwrap(); - handle.enable(irq_num as u32); - handle - }); - } - - /// Gets the remapping index of the IRQ line. - /// - /// This method will return `None` if interrupt remapping is disabled or - /// not supported by the architecture. - pub(crate) fn remapping_index(&self) -> Option { - Some(self.entry.get()?.index()) - } -} - -// FIXME: Mark this as unsafe. See -// . -pub(crate) fn enable_local() { - x86_64::instructions::interrupts::enable(); - // When emulated with QEMU, interrupts may not be delivered if a STI instruction is immediately - // followed by a RET instruction. It is a BUG of QEMU, see the following patch for details. - // https://lore.kernel.org/qemu-devel/20231210190147.129734-2-lrh2000@pku.edu.cn/ - x86_64::instructions::nop(); -} - -/// Enables local IRQs and halts the CPU to wait for interrupts. -/// -/// This method guarantees that no interrupts can occur in the middle. In other words, IRQs must -/// either have been processed before this method is called, or they must wake the CPU up from the -/// halting state. -// -// FIXME: Mark this as unsafe. See -// . -pub(crate) fn enable_local_and_halt() { - // SAFETY: - // 1. `sti` is safe to use because its safety requirement is upheld by the caller. - // 2. `hlt` is safe to use because it halts the CPU for interrupts. - unsafe { - // Intel(R) 64 and IA-32 Architectures Software Developer's Manual says: - // "If IF = 0, maskable hardware interrupts remain inhibited on the instruction boundary - // following an execution of STI." - // - // So interrupts will only occur at or after the HLT instruction, which guarantee that - // interrupts won't occur between enabling the local IRQs and halting the CPU. - core::arch::asm!("sti", "hlt", options(nomem, nostack, preserves_flags),) - }; -} - -pub(crate) fn disable_local() { - x86_64::instructions::interrupts::disable(); -} - -pub(crate) fn is_local_enabled() -> bool { - (rflags::read_raw() & RFlags::INTERRUPT_FLAG.bits()) != 0 -} - -// ####### Inter-Processor Interrupts (IPIs) ####### - -/// Hardware-specific, architecture-dependent CPU ID. -/// -/// This is the Local APIC ID in the x86_64 architecture. -#[derive(Debug, Clone, Copy, PartialEq, Eq)] -pub(crate) struct HwCpuId(u32); - -impl HwCpuId { - pub(crate) fn read_current(guard: &dyn PinCurrentCpu) -> Self { - use crate::arch::kernel::apic; - - let apic = apic::get_or_init(guard); - Self(apic.id()) - } -} - -/// Sends a general inter-processor interrupt (IPI) to the specified CPU. -/// -/// # Safety -/// -/// The caller must ensure that the interrupt number is valid and that -/// the corresponding handler is configured correctly on the remote CPU. -/// Furthermore, invoking the interrupt handler must also be safe. -pub(crate) unsafe fn send_ipi(hw_cpu_id: HwCpuId, irq_num: u8, guard: &dyn PinCurrentCpu) { - use crate::arch::kernel::apic::{self, Icr}; - - let icr = Icr::new( - apic::ApicId::from(hw_cpu_id.0), - apic::DestinationShorthand::NoShorthand, - apic::TriggerMode::Edge, - apic::Level::Assert, - apic::DeliveryStatus::Idle, - apic::DestinationMode::Physical, - apic::DeliveryMode::Fixed, - irq_num, - ); - - let apic = apic::get_or_init(guard); - // SAFETY: The ICR is valid to generate the request IPI. Generating the request IPI is safe - // as guaranteed by the caller. - unsafe { apic.send_ipi(icr) }; -} diff --git a/ostd/src/arch/x86/kernel/irq/ioapic.rs b/ostd/src/arch/x86/irq/chip/ioapic.rs similarity index 100% rename from ostd/src/arch/x86/kernel/irq/ioapic.rs rename to ostd/src/arch/x86/irq/chip/ioapic.rs diff --git a/ostd/src/arch/x86/kernel/irq/mod.rs b/ostd/src/arch/x86/irq/chip/mod.rs similarity index 98% rename from ostd/src/arch/x86/kernel/irq/mod.rs rename to ostd/src/arch/x86/irq/chip/mod.rs index a4f3d57bd..8ce25d996 100644 --- a/ostd/src/arch/x86/kernel/irq/mod.rs +++ b/ostd/src/arch/x86/irq/chip/mod.rs @@ -10,8 +10,10 @@ use ioapic::IoApic; use log::info; use spin::Once; -use super::acpi::get_acpi_tables; -use crate::{io::IoMemAllocatorBuilder, irq::IrqLine, sync::SpinLock, Error, Result}; +use crate::{ + arch::kernel::acpi::get_acpi_tables, io::IoMemAllocatorBuilder, irq::IrqLine, sync::SpinLock, + Error, Result, +}; mod ioapic; mod pic; diff --git a/ostd/src/arch/x86/kernel/irq/pic.rs b/ostd/src/arch/x86/irq/chip/pic.rs similarity index 100% rename from ostd/src/arch/x86/kernel/irq/pic.rs rename to ostd/src/arch/x86/irq/chip/pic.rs diff --git a/ostd/src/arch/x86/irq/ipi.rs b/ostd/src/arch/x86/irq/ipi.rs new file mode 100644 index 000000000..638c49ac4 --- /dev/null +++ b/ostd/src/arch/x86/irq/ipi.rs @@ -0,0 +1,47 @@ +// SPDX-License-Identifier: MPL-2.0 + +//! Inter-processor interrupts. + +use crate::cpu::PinCurrentCpu; + +/// Hardware-specific, architecture-dependent CPU ID. +/// +/// This is the Local APIC ID in the x86_64 architecture. +#[derive(Debug, Clone, Copy, PartialEq, Eq)] +pub(crate) struct HwCpuId(u32); + +impl HwCpuId { + pub(crate) fn read_current(guard: &dyn PinCurrentCpu) -> Self { + use crate::arch::kernel::apic; + + let apic = apic::get_or_init(guard); + Self(apic.id()) + } +} + +/// Sends a general inter-processor interrupt (IPI) to the specified CPU. +/// +/// # Safety +/// +/// The caller must ensure that the interrupt number is valid and that +/// the corresponding handler is configured correctly on the remote CPU. +/// Furthermore, invoking the interrupt handler must also be safe. +pub(crate) unsafe fn send_ipi(hw_cpu_id: HwCpuId, irq_num: u8, guard: &dyn PinCurrentCpu) { + use crate::arch::kernel::apic::{self, Icr}; + + let icr = Icr::new( + apic::ApicId::from(hw_cpu_id.0), + apic::DestinationShorthand::NoShorthand, + apic::TriggerMode::Edge, + apic::Level::Assert, + apic::DeliveryStatus::Idle, + apic::DestinationMode::Physical, + apic::DeliveryMode::Fixed, + irq_num, + ); + + let apic = apic::get_or_init(guard); + // SAFETY: The ICR is valid to generate the request IPI. Generating the request IPI is safe + // as guaranteed by the caller. + unsafe { apic.send_ipi(icr) }; +} diff --git a/ostd/src/arch/x86/irq/mod.rs b/ostd/src/arch/x86/irq/mod.rs new file mode 100644 index 000000000..ea3da8bf6 --- /dev/null +++ b/ostd/src/arch/x86/irq/mod.rs @@ -0,0 +1,20 @@ +// SPDX-License-Identifier: MPL-2.0 + +//! Interrupts. + +pub(super) mod chip; +mod ipi; +mod ops; +mod remapping; + +pub use chip::{IrqChip, MappedIrqLine, IRQ_CHIP}; +pub(crate) use ipi::{send_ipi, HwCpuId}; +pub(crate) use ops::{disable_local, enable_local, enable_local_and_halt, is_local_enabled}; +pub(crate) use remapping::IrqRemapping; + +// Intel(R) 64 and IA-32 rchitectures Software Developer's Manual, +// Volume 3A, Section 6.2 says "Vector numbers in the range 32 to 255 +// are designated as user-defined interrupts and are not reserved by +// the Intel 64 and IA-32 architecture." +pub(crate) const IRQ_NUM_MIN: u8 = 32; +pub(crate) const IRQ_NUM_MAX: u8 = 255; diff --git a/ostd/src/arch/x86/irq/ops.rs b/ostd/src/arch/x86/irq/ops.rs new file mode 100644 index 000000000..7cb7450c6 --- /dev/null +++ b/ostd/src/arch/x86/irq/ops.rs @@ -0,0 +1,46 @@ +// SPDX-License-Identifier: MPL-2.0 + +//! Interrupt operations. + +use x86_64::registers::rflags::{self, RFlags}; + +// FIXME: Mark this as unsafe. See +// . +pub(crate) fn enable_local() { + x86_64::instructions::interrupts::enable(); + // When emulated with QEMU, interrupts may not be delivered if a STI instruction is immediately + // followed by a RET instruction. It is a BUG of QEMU, see the following patch for details. + // https://lore.kernel.org/qemu-devel/20231210190147.129734-2-lrh2000@pku.edu.cn/ + x86_64::instructions::nop(); +} + +/// Enables local IRQs and halts the CPU to wait for interrupts. +/// +/// This method guarantees that no interrupts can occur in the middle. In other words, IRQs must +/// either have been processed before this method is called, or they must wake the CPU up from the +/// halting state. +// +// FIXME: Mark this as unsafe. See +// . +pub(crate) fn enable_local_and_halt() { + // SAFETY: + // 1. `sti` is safe to use because its safety requirement is upheld by the caller. + // 2. `hlt` is safe to use because it halts the CPU for interrupts. + unsafe { + // Intel(R) 64 and IA-32 Architectures Software Developer's Manual says: + // "If IF = 0, maskable hardware interrupts remain inhibited on the instruction boundary + // following an execution of STI." + // + // So interrupts will only occur at or after the HLT instruction, which guarantee that + // interrupts won't occur between enabling the local IRQs and halting the CPU. + core::arch::asm!("sti", "hlt", options(nomem, nostack, preserves_flags),) + }; +} + +pub(crate) fn disable_local() { + x86_64::instructions::interrupts::disable(); +} + +pub(crate) fn is_local_enabled() -> bool { + (rflags::read_raw() & RFlags::INTERRUPT_FLAG.bits()) != 0 +} diff --git a/ostd/src/arch/x86/irq/remapping.rs b/ostd/src/arch/x86/irq/remapping.rs new file mode 100644 index 000000000..71172b78f --- /dev/null +++ b/ostd/src/arch/x86/irq/remapping.rs @@ -0,0 +1,40 @@ +// SPDX-License-Identifier: MPL-2.0 + +use spin::once::Once; + +use crate::arch::iommu::{alloc_irt_entry, has_interrupt_remapping, IrtEntryHandle}; + +pub(crate) struct IrqRemapping { + entry: Once, +} + +impl IrqRemapping { + pub(crate) const fn new() -> Self { + Self { entry: Once::new() } + } + + /// Initializes the remapping entry for the specific IRQ number. + /// + /// This will do nothing if the entry is already initialized or interrupt + /// remapping is disabled or not supported by the architecture. + pub(crate) fn init(&self, irq_num: u8) { + if !has_interrupt_remapping() { + return; + } + + self.entry.call_once(|| { + // Allocate and enable the IRT entry. + let handle = alloc_irt_entry().unwrap(); + handle.enable(irq_num as u32); + handle + }); + } + + /// Gets the remapping index of the IRQ line. + /// + /// This method will return `None` if interrupt remapping is disabled or + /// not supported by the architecture. + pub(crate) fn remapping_index(&self) -> Option { + Some(self.entry.get()?.index()) + } +} diff --git a/ostd/src/arch/x86/kernel/mod.rs b/ostd/src/arch/x86/kernel/mod.rs index 8130116f7..168c1baa2 100644 --- a/ostd/src/arch/x86/kernel/mod.rs +++ b/ostd/src/arch/x86/kernel/mod.rs @@ -7,7 +7,4 @@ pub(super) mod acpi; pub(super) mod apic; -pub(super) mod irq; pub(super) mod tsc; - -pub use irq::{IrqChip, MappedIrqLine, IRQ_CHIP}; diff --git a/ostd/src/arch/x86/mod.rs b/ostd/src/arch/x86/mod.rs index c2cd0993e..b90526ce7 100644 --- a/ostd/src/arch/x86/mod.rs +++ b/ostd/src/arch/x86/mod.rs @@ -8,8 +8,8 @@ pub mod device; pub(crate) mod ex_table; pub(crate) mod io; pub(crate) mod iommu; -pub(crate) mod irq; -pub mod kernel; +pub mod irq; +mod kernel; pub(crate) mod mm; pub mod qemu; pub(crate) mod serial; @@ -56,7 +56,7 @@ pub(crate) unsafe fn late_init_on_bsp() { let io_mem_builder = io::construct_io_mem_allocator_builder(); kernel::apic::init(&io_mem_builder).expect("APIC doesn't exist"); - kernel::irq::init(&io_mem_builder); + irq::chip::init(&io_mem_builder); kernel::tsc::init_tsc_freq(); timer::init_on_bsp(); diff --git a/ostd/src/arch/x86/timer/hpet.rs b/ostd/src/arch/x86/timer/hpet.rs index 39f057066..2eabfcbac 100644 --- a/ostd/src/arch/x86/timer/hpet.rs +++ b/ostd/src/arch/x86/timer/hpet.rs @@ -11,7 +11,10 @@ use volatile::{ }; use crate::{ - arch::kernel::{acpi::get_acpi_tables, MappedIrqLine, IRQ_CHIP}, + arch::{ + irq::{MappedIrqLine, IRQ_CHIP}, + kernel::acpi::get_acpi_tables, + }, irq::IrqLine, mm::paddr_to_vaddr, }; diff --git a/ostd/src/arch/x86/timer/pit.rs b/ostd/src/arch/x86/timer/pit.rs index 59e630d97..eaf9face6 100644 --- a/ostd/src/arch/x86/timer/pit.rs +++ b/ostd/src/arch/x86/timer/pit.rs @@ -12,7 +12,7 @@ use crate::{ arch::{ device::io_port::WriteOnlyAccess, - kernel::{MappedIrqLine, IRQ_CHIP}, + irq::{MappedIrqLine, IRQ_CHIP}, }, io::{sensitive_io_port, IoPort}, irq::IrqLine,