Reorangize `ostd::arch::irq`

This commit is contained in:
Ruihan Li 2025-10-12 23:31:07 +08:00 committed by Tate, Hongliang Tian
parent f69d39e9c1
commit bc6ef5231b
31 changed files with 379 additions and 339 deletions

View File

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

View File

@ -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.

View File

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

View File

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

View File

@ -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<u16> {
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
// <https://github.com/asterinas/asterinas/issues/1120#issuecomment-2748696592>.
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!()
}

View File

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

View File

@ -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!()
}

View File

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

View File

@ -0,0 +1,33 @@
// SPDX-License-Identifier: MPL-2.0
//! Interrupt operations.
// FIXME: Mark this as unsafe. See
// <https://github.com/asterinas/asterinas/issues/1120#issuecomment-2748696592>.
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
// <https://github.com/asterinas/asterinas/issues/1120#issuecomment-2748696592>.
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()
}

View File

@ -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<u16> {
None
}
}

View File

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

View File

@ -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.

View File

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

View File

@ -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<u16> {
None
}
}
// FIXME: Mark this as unsafe. See
// <https://github.com/asterinas/asterinas/issues/1120#issuecomment-2748696592>.
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
// <https://github.com/asterinas/asterinas/issues/1120#issuecomment-2748696592>.
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!()
}

View File

@ -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!()
}

View File

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

View File

@ -0,0 +1,39 @@
// SPDX-License-Identifier: MPL-2.0
//! Interrupt operations.
// FIXME: Mark this as unsafe. See
// <https://github.com/asterinas/asterinas/issues/1120#issuecomment-2748696592>.
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
// <https://github.com/asterinas/asterinas/issues/1120#issuecomment-2748696592>.
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()
}

View File

@ -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<u16> {
None
}
}

View File

@ -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<IrtEntryHandle>,
}
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<u16> {
Some(self.entry.get()?.index())
}
}
// FIXME: Mark this as unsafe. See
// <https://github.com/asterinas/asterinas/issues/1120#issuecomment-2748696592>.
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
// <https://github.com/asterinas/asterinas/issues/1120#issuecomment-2748696592>.
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) };
}

View File

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

View File

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

View File

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

View File

@ -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
// <https://github.com/asterinas/asterinas/issues/1120#issuecomment-2748696592>.
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
// <https://github.com/asterinas/asterinas/issues/1120#issuecomment-2748696592>.
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
}

View File

@ -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<IrtEntryHandle>,
}
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<u16> {
Some(self.entry.get()?.index())
}
}

View File

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

View File

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

View File

@ -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,
};

View File

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