diff --git a/kernel/src/process/process/timer_manager.rs b/kernel/src/process/process/timer_manager.rs index 440f69941..245191734 100644 --- a/kernel/src/process/process/timer_manager.rs +++ b/kernel/src/process/process/timer_manager.rs @@ -7,7 +7,7 @@ use alloc::{ }; use id_alloc::IdAlloc; -use ostd::{arch::trap::is_kernel_interrupted, sync::Mutex, timer}; +use ostd::{cpu::PrivilegeLevel, irq::InterruptLevel, sync::Mutex, timer}; use super::Process; use crate::{ @@ -31,6 +31,19 @@ use crate::{ /// invoke the callbacks of expired timers which are based on the updated /// CPU clock. fn update_cpu_time() { + // Retrieve some info about the timer interrupt + let is_kernel_interrupted = { + let interrupt_level = InterruptLevel::current(); + let InterruptLevel::L1(cpu_priv_at_irq) = interrupt_level else { + // We are at the interrupt level 2. + // This means that bottom half of IRQ handling is interrupted. + // We should not count this time slice on the head of the current task. + return; + }; + cpu_priv_at_irq == PrivilegeLevel::Kernel + }; + + // Retrieve some info about the current task let Some(current_thread) = Thread::current() else { return; }; @@ -42,11 +55,12 @@ fn update_cpu_time() { // `None` here. return; }; + let timer_manager = process.timer_manager(); // Based on whether the timer interrupt occurs in kernel mode or user mode, // the function will add the duration of one timer interrupt interval to the // corresponding CPU clocks. - if is_kernel_interrupted() { + if is_kernel_interrupted { posix_thread.prof_clock().kernel_clock().add_jiffies(1); process.prof_clock().kernel_clock().add_jiffies(1); } else { diff --git a/ostd/src/arch/loongarch/cpu/context.rs b/ostd/src/arch/loongarch/cpu/context.rs index 4c38bc343..331c61d02 100644 --- a/ostd/src/arch/loongarch/cpu/context.rs +++ b/ostd/src/arch/loongarch/cpu/context.rs @@ -11,6 +11,7 @@ use crate::{ mm::tlb_flush_addr, trap::{RawUserContext, TrapFrame}, }, + cpu::PrivilegeLevel, irq::call_irq_callback_functions, user::{ReturnReason, UserContextApi, UserContextApiInternal}, }; @@ -219,7 +220,7 @@ impl UserContextApiInternal for UserContext { log::debug!("Handling hardware interrupt: {:?}", interrupt); while let Some(irq) = crate::arch::kernel::irq::claim() { // Call the IRQ callback functions for the claimed interrupt - call_irq_callback_functions(&self.as_trap_frame(), irq as _); + call_irq_callback_functions(&self.as_trap_frame(), irq as _, PrivilegeLevel::User); } } Interrupt::PMI => todo!(), diff --git a/ostd/src/arch/loongarch/trap/mod.rs b/ostd/src/arch/loongarch/trap/mod.rs index ca507fcd6..1f446440c 100644 --- a/ostd/src/arch/loongarch/trap/mod.rs +++ b/ostd/src/arch/loongarch/trap/mod.rs @@ -12,15 +12,11 @@ pub use trap::TrapFrame; use crate::{ arch::{cpu::context::CpuExceptionInfo, mm::tlb_flush_addr}, - cpu_local_cell, + cpu::PrivilegeLevel, irq::call_irq_callback_functions, mm::MAX_USERSPACE_VADDR, }; -cpu_local_cell! { - static IS_KERNEL_INTERRUPTED: bool = false; -} - /// Initialize trap handling on LoongArch. pub(crate) unsafe fn init() { unsafe { @@ -28,13 +24,6 @@ pub(crate) unsafe fn init() { } } -/// Returns true if this function is called within the context of an IRQ handler -/// and the IRQ occurs while the CPU is executing in the kernel mode. -/// Otherwise, it returns false. -pub fn is_kernel_interrupted() -> bool { - IS_KERNEL_INTERRUPTED.load() -} - /// Handle traps (only from kernel). #[no_mangle] extern "C" fn trap_handler(f: &mut TrapFrame) { @@ -85,7 +74,6 @@ extern "C" fn trap_handler(f: &mut TrapFrame) { Exception::TLBRFill => unreachable!(), }, Trap::Interrupt(interrupt) => { - IS_KERNEL_INTERRUPTED.store(true); match interrupt { Interrupt::SWI0 => todo!(), Interrupt::SWI1 => todo!(), @@ -100,14 +88,13 @@ extern "C" fn trap_handler(f: &mut TrapFrame) { log::debug!("Handling hardware interrupt: {:?}", interrupt); while let Some(irq) = crate::arch::kernel::irq::claim() { // Call the IRQ callback functions for the claimed interrupt - call_irq_callback_functions(f, irq as _); + call_irq_callback_functions(f, irq as _, PrivilegeLevel::Kernel); } } Interrupt::PMI => todo!(), Interrupt::Timer => todo!(), Interrupt::IPI => todo!(), } - IS_KERNEL_INTERRUPTED.store(false); } Trap::MachineError(machine_error) => panic!( "Machine error: {machine_error:?}, badv: {badv:#x?}, badi: {badi:#x?}, era: {era:#x?}" diff --git a/ostd/src/arch/riscv/cpu/context.rs b/ostd/src/arch/riscv/cpu/context.rs index 6810635aa..e6fb66782 100644 --- a/ostd/src/arch/riscv/cpu/context.rs +++ b/ostd/src/arch/riscv/cpu/context.rs @@ -11,6 +11,7 @@ use crate::{ trap::{RawUserContext, TrapFrame}, TIMER_IRQ_NUM, }, + cpu::PrivilegeLevel, irq::call_irq_callback_functions, user::{ReturnReason, UserContextApi, UserContextApiInternal}, }; @@ -149,6 +150,7 @@ impl UserContextApiInternal for UserContext { call_irq_callback_functions( &self.as_trap_frame(), TIMER_IRQ_NUM.load(Ordering::Relaxed) as usize, + PrivilegeLevel::User, ); } Trap::Interrupt(_) => todo!(), diff --git a/ostd/src/arch/riscv/trap/mod.rs b/ostd/src/arch/riscv/trap/mod.rs index 4354bf5a1..b2253f42d 100644 --- a/ostd/src/arch/riscv/trap/mod.rs +++ b/ostd/src/arch/riscv/trap/mod.rs @@ -13,11 +13,7 @@ pub(super) use trap::RawUserContext; pub use trap::TrapFrame; use super::{cpu::context::CpuExceptionInfo, timer::TIMER_IRQ_NUM}; -use crate::{cpu_local_cell, irq::call_irq_callback_functions}; - -cpu_local_cell! { - static IS_KERNEL_INTERRUPTED: bool = false; -} +use crate::{cpu::PrivilegeLevel, irq::call_irq_callback_functions}; /// Initializes interrupt handling on RISC-V. pub(crate) unsafe fn init() { @@ -26,35 +22,28 @@ pub(crate) unsafe fn init() { } } -/// Returns true if this function is called within the context of an IRQ handler -/// and the IRQ occurs while the CPU is executing in the kernel mode. -/// Otherwise, it returns false. -pub fn is_kernel_interrupted() -> bool { - IS_KERNEL_INTERRUPTED.load() -} - /// Handle traps (only from kernel). #[no_mangle] extern "C" fn trap_handler(f: &mut TrapFrame) { use riscv::register::scause::Trap; match riscv::register::scause::read().cause() { - Trap::Interrupt(interrupt) => { - IS_KERNEL_INTERRUPTED.store(true); - match interrupt { - Interrupt::SupervisorTimer => { - call_irq_callback_functions(f, TIMER_IRQ_NUM.load(Ordering::Relaxed) as usize); - } - Interrupt::SupervisorExternal => todo!(), - Interrupt::SupervisorSoft => todo!(), - _ => { - panic!( + Trap::Interrupt(interrupt) => match interrupt { + Interrupt::SupervisorTimer => { + call_irq_callback_functions( + f, + TIMER_IRQ_NUM.load(Ordering::Relaxed) as usize, + PrivilegeLevel::Kernel, + ); + } + Interrupt::SupervisorExternal => todo!(), + Interrupt::SupervisorSoft => todo!(), + _ => { + panic!( "cannot handle unknown supervisor interrupt: {interrupt:?}. trapframe: {f:#x?}.", ); - } } - IS_KERNEL_INTERRUPTED.store(false); - } + }, Trap::Exception(e) => { let stval = riscv::register::stval::read(); panic!( diff --git a/ostd/src/arch/x86/cpu/context/mod.rs b/ostd/src/arch/x86/cpu/context/mod.rs index 7863f993b..3484f2501 100644 --- a/ostd/src/arch/x86/cpu/context/mod.rs +++ b/ostd/src/arch/x86/cpu/context/mod.rs @@ -19,6 +19,7 @@ use x86_64::registers::{ use crate::{ arch::trap::{RawUserContext, TrapFrame}, + cpu::PrivilegeLevel, irq::call_irq_callback_functions, mm::Vaddr, task::scheduler, @@ -304,6 +305,7 @@ impl UserContextApiInternal for UserContext { call_irq_callback_functions( &self.as_trap_frame(), self.as_trap_frame().trap_num, + PrivilegeLevel::User, ); crate::arch::irq::enable_local(); } diff --git a/ostd/src/arch/x86/trap/mod.rs b/ostd/src/arch/x86/trap/mod.rs index 9c6c6b7cd..0cf8c20d1 100644 --- a/ostd/src/arch/x86/trap/mod.rs +++ b/ostd/src/arch/x86/trap/mod.rs @@ -32,7 +32,7 @@ use crate::{ if_tdx_enabled, irq::{disable_local, enable_local}, }, - cpu_local_cell, + cpu::PrivilegeLevel, irq::call_irq_callback_functions, mm::{ kspace::{KERNEL_PAGE_TABLE, LINEAR_MAPPING_BASE_VADDR, LINEAR_MAPPING_VADDR_RANGE}, @@ -49,10 +49,6 @@ cfg_if! { } } -cpu_local_cell! { - static KERNEL_INTERRUPT_NESTED_LEVEL: u8 = 0; -} - /// Trap frame of kernel interrupt /// /// # Trap handler @@ -138,13 +134,6 @@ pub(super) struct RawUserContext { pub(super) error_code: usize, } -/// Returns true if this function is called within the context of an IRQ handler -/// and the IRQ occurs while the CPU is executing in the kernel mode. -/// Otherwise, it returns false. -pub fn is_kernel_interrupted() -> bool { - KERNEL_INTERRUPT_NESTED_LEVEL.load() != 0 -} - /// Handle traps (only from kernel). #[no_mangle] extern "sysv64" fn trap_handler(f: &mut TrapFrame) { @@ -197,9 +186,7 @@ extern "sysv64" fn trap_handler(f: &mut TrapFrame) { ); } None => { - KERNEL_INTERRUPT_NESTED_LEVEL.add_assign(1); - call_irq_callback_functions(f, f.trap_num); - KERNEL_INTERRUPT_NESTED_LEVEL.sub_assign(1); + call_irq_callback_functions(f, f.trap_num, PrivilegeLevel::Kernel); } } } diff --git a/ostd/src/cpu/mod.rs b/ostd/src/cpu/mod.rs index 504338cae..365fa5196 100644 --- a/ostd/src/cpu/mod.rs +++ b/ostd/src/cpu/mod.rs @@ -56,6 +56,16 @@ impl TryFrom for CpuId { } } +/// The CPU privilege level: user mode or kernel mode. +#[derive(Copy, Clone, Debug, PartialEq, Eq, PartialOrd, Ord)] +#[repr(u8)] +pub enum PrivilegeLevel { + /// User mode. + User = 0, + /// Kernel mode. + Kernel = 1, +} + /// The number of CPUs. static mut NUM_CPUS: u32 = 1; diff --git a/ostd/src/irq/bottom_half.rs b/ostd/src/irq/bottom_half.rs index 656fd168f..420846e75 100644 --- a/ostd/src/irq/bottom_half.rs +++ b/ostd/src/irq/bottom_half.rs @@ -37,7 +37,7 @@ static BOTTOM_HALF_HANDLER_L2: Once = Once::new(); pub(super) fn process() { match InterruptLevel::current() { - InterruptLevel::L1 => process_l1(), + InterruptLevel::L1(_) => process_l1(), InterruptLevel::L2 => process_l2(), _ => unreachable!("this function must have been call in interrupt context"), } diff --git a/ostd/src/irq/level.rs b/ostd/src/irq/level.rs index dfc8b5c83..691c73b3e 100644 --- a/ostd/src/irq/level.rs +++ b/ostd/src/irq/level.rs @@ -2,34 +2,30 @@ //! The interrupt level. -use crate::cpu_local_cell; - -// Enter the scope of interrupt handling, -// increasing the interrupt level by one. -pub(super) fn enter(f: F) { - INTERRUPT_LEVEL.add_assign(1); - - f(); - - INTERRUPT_LEVEL.sub_assign(1); -} +use crate::{cpu::PrivilegeLevel, cpu_local_cell}; /// The current interrupt level on a CPU. /// /// This type tracks the current nesting depth on the CPU /// where the code is executing. +/// There are three levels: +/// * Level 0 (the task context); +/// * Level 1 (the interrupt context); +/// * Level 2 (the interrupt context due to nested interrupts). +/// /// An `InterruptLevel` is specific to a single CPU /// and is meaningless when used by or sent to other CPUs, /// hence it is `!Send` and `!Sync`. -#[derive(Copy, Clone, Debug, PartialEq, Eq, PartialOrd, Ord)] -#[repr(u8)] +#[derive(Copy, Clone, Debug, PartialEq, Eq)] pub enum InterruptLevel { /// Level 0 (the task context). - L0 = 0, + L0, /// Level 1 (the interrupt context). - L1 = 1, + /// + /// The intern value specifies the CPU privilege level of the interrupted code. + L1(PrivilegeLevel), /// Level 2 (the interrupt context due to nested interrupts). - L2 = 2, + L2, } impl !Send for InterruptLevel {} @@ -38,15 +34,36 @@ impl !Sync for InterruptLevel {} impl InterruptLevel { /// Returns the current interrupt level of this CPU. pub fn current() -> Self { - let level = INTERRUPT_LEVEL.load(); + // Parameters about the encoding of INTERRUPT_LEVEL + const LEVEL_VAL_OFFSET: u8 = 1; + const CPU_PRIV_MASK: u8 = 1 << 0; + + let raw_level = INTERRUPT_LEVEL.load(); + let level = raw_level >> LEVEL_VAL_OFFSET; match level { 0 => Self::L0, - 1 => Self::L1, + 1 => { + let cpu_priv_at_irq = if (raw_level & CPU_PRIV_MASK) == 0 { + PrivilegeLevel::Kernel + } else { + PrivilegeLevel::User + }; + Self::L1(cpu_priv_at_irq) + } 2 => Self::L2, _ => unreachable!("level must between 0 and 2 (inclusive)"), } } + /// Returns the interrupt level as an integer between 0 and 2 (inclusive). + pub fn as_u8(&self) -> u8 { + match self { + Self::L0 => 0, + Self::L1(_) => 1, + Self::L2 => 2, + } + } + /// Checks if the CPU is currently in the task context (level 0). pub fn is_task_context(&self) -> bool { *self == Self::L0 @@ -54,10 +71,50 @@ impl InterruptLevel { /// Checks if the CPU is currently in the interrupt context (level 1 or 2). pub fn is_interrupt_context(&self) -> bool { - *self == Self::L1 || *self == Self::L2 + matches!(self, Self::L1(_) | Self::L2) } } +/// Enters the scope of interrupt handling, +/// increasing the interrupt level by one. +/// +/// The `cpu_priv_at_irq` argument specifies the CPU privilege level of +/// the code interrupted by the IRQ. +pub(super) fn enter(f: F, cpu_priv_at_irq: PrivilegeLevel) { + let increment = { + let bit_0 = match cpu_priv_at_irq { + PrivilegeLevel::Kernel => 0, + PrivilegeLevel::User => 1, + }; + let bit_1 = 0b10; + bit_1 | bit_0 + }; + INTERRUPT_LEVEL.add_assign(increment); + + f(); + + INTERRUPT_LEVEL.sub_assign(increment); +} + cpu_local_cell! { + /// The interrupt level of the current IRQ. + /// + /// We pack two pieces of information into a single byte: + /// 1. The current interrupt level (bit 1 - 7); + /// 2. The CPU privilege level of the code interrupted by the IRQ (bit 0). + /// + /// More specifically, + /// the encoding of this byte is summarized in the table below. + /// + /// | Values | Meaning | + /// |----------|---------------------| + /// | `0b00_0` | L0 | + /// | `0b01_0` | L1 from kernel | + /// | `0b01_1` | L1 from user | + /// | `0b10_0` | L2 (L1 from kernel) | + /// | `0b10_1` | L2 (L1 from user) | + /// + /// This compact encoding allows us to update this value + /// in a single arithematic operation (see `enter`). static INTERRUPT_LEVEL: u8 = 0; } diff --git a/ostd/src/irq/mod.rs b/ostd/src/irq/mod.rs index f0a0512ee..8281333ce 100644 --- a/ostd/src/irq/mod.rs +++ b/ostd/src/irq/mod.rs @@ -81,11 +81,18 @@ pub use guard::{disable_local, DisabledLocalIrqGuard}; pub use level::InterruptLevel; pub use top_half::{IrqCallbackFunction, IrqLine}; -use crate::arch::trap::TrapFrame; +use crate::{arch::trap::TrapFrame, cpu::PrivilegeLevel}; -pub(crate) fn call_irq_callback_functions(trap_frame: &TrapFrame, irq_num: usize) { - level::enter(move || { - top_half::process(trap_frame, irq_num); - bottom_half::process(); - }); +pub(crate) fn call_irq_callback_functions( + trap_frame: &TrapFrame, + irq_num: usize, + cpu_priv_at_irq: PrivilegeLevel, +) { + level::enter( + move || { + top_half::process(trap_frame, irq_num); + bottom_half::process(); + }, + cpu_priv_at_irq, + ); }