Reorangize `ostd::arch::irq`
This commit is contained in:
parent
f69d39e9c1
commit
bc6ef5231b
|
|
@ -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,
|
||||
|
|
|
|||
|
|
@ -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.
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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!()
|
||||
}
|
||||
|
|
@ -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
|
||||
|
|
@ -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!()
|
||||
}
|
||||
|
|
@ -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;
|
||||
|
|
@ -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()
|
||||
}
|
||||
|
|
@ -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
|
||||
}
|
||||
}
|
||||
|
|
@ -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;
|
||||
|
|
@ -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.
|
||||
|
|
|
|||
|
|
@ -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);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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!()
|
||||
}
|
||||
|
|
@ -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!()
|
||||
}
|
||||
|
|
@ -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;
|
||||
|
|
@ -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()
|
||||
}
|
||||
|
|
@ -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
|
||||
}
|
||||
}
|
||||
|
|
@ -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) };
|
||||
}
|
||||
|
|
@ -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;
|
||||
|
|
@ -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) };
|
||||
}
|
||||
|
|
@ -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;
|
||||
|
|
@ -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
|
||||
}
|
||||
|
|
@ -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())
|
||||
}
|
||||
}
|
||||
|
|
@ -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};
|
||||
|
|
|
|||
|
|
@ -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();
|
||||
|
|
|
|||
|
|
@ -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,
|
||||
};
|
||||
|
|
|
|||
|
|
@ -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,
|
||||
|
|
|
|||
Loading…
Reference in New Issue