diff --git a/kernel/src/process/clone.rs b/kernel/src/process/clone.rs index c48a39679..5c6ded926 100644 --- a/kernel/src/process/clone.rs +++ b/kernel/src/process/clone.rs @@ -237,15 +237,18 @@ fn clone_child_task( .. } = ctx; - // clone system V semaphore + // Clone system V semaphore clone_sysvsem(clone_flags)?; - // clone file table + // Clone file table let child_file_table = clone_files(thread_local.borrow_file_table().unwrap(), clone_flags); - // clone fs + // Clone fs let child_fs = clone_fs(posix_thread.fs(), clone_flags); + // Clone FPU context + let child_fpu_context = thread_local.fpu().clone_context(); + let child_user_ctx = Arc::new(clone_user_ctx( parent_context, clone_args.stack, @@ -272,7 +275,8 @@ fn clone_child_task( .thread_name(thread_name) .sig_mask(sig_mask) .file_table(child_file_table) - .fs(child_fs); + .fs(child_fs) + .fpu_context(child_fpu_context); // Deal with SETTID/CLEARTID flags clone_parent_settid(child_tid, clone_args.parent_tid, clone_flags)?; @@ -332,6 +336,9 @@ fn clone_child_process( // Clone System V semaphore clone_sysvsem(clone_flags)?; + // Clone FPU context + let child_fpu_context = thread_local.fpu().clone_context(); + // Inherit the parent's signal mask let child_sig_mask = posix_thread.sig_mask().load(Ordering::Relaxed).into(); @@ -358,6 +365,7 @@ fn clone_child_process( .sig_mask(child_sig_mask) .file_table(child_file_table) .fs(child_fs) + .fpu_context(child_fpu_context) }; // Deal with SETTID/CLEARTID flags @@ -474,10 +482,6 @@ fn clone_user_ctx( child_context.set_tls_pointer(tls as usize); } - // New threads inherit the FPU state of the parent thread and - // the state is private to the thread thereafter. - child_context.fpu_state().save(); - child_context } diff --git a/kernel/src/process/posix_thread/builder.rs b/kernel/src/process/posix_thread/builder.rs index 40a84325d..d50034087 100644 --- a/kernel/src/process/posix_thread/builder.rs +++ b/kernel/src/process/posix_thread/builder.rs @@ -1,7 +1,10 @@ // SPDX-License-Identifier: MPL-2.0 use ostd::{ - cpu::{context::UserContext, CpuSet}, + cpu::{ + context::{FpuContext, UserContext}, + CpuSet, + }, sync::RwArc, task::Task, }; @@ -37,6 +40,7 @@ pub struct PosixThreadBuilder { sig_mask: AtomicSigMask, sig_queues: SigQueues, sched_policy: SchedPolicy, + fpu_context: FpuContext, } impl PosixThreadBuilder { @@ -54,6 +58,7 @@ impl PosixThreadBuilder { sig_mask: AtomicSigMask::new_empty(), sig_queues: SigQueues::new(), sched_policy: SchedPolicy::Fair(Nice::default()), + fpu_context: FpuContext::new(), } } @@ -92,6 +97,11 @@ impl PosixThreadBuilder { self } + pub fn fpu_context(mut self, fpu_context: FpuContext) -> Self { + self.fpu_context = fpu_context; + self + } + pub fn build(self) -> Arc { let Self { tid, @@ -106,6 +116,7 @@ impl PosixThreadBuilder { sig_mask, sig_queues, sched_policy, + fpu_context, } = self; let file_table = file_table.unwrap_or_else(|| RwArc::new(FileTable::new_with_stdio())); @@ -150,8 +161,13 @@ impl PosixThreadBuilder { sched_policy, )); - let thread_local = - ThreadLocal::new(set_child_tid, clear_child_tid, root_vmar, file_table); + let thread_local = ThreadLocal::new( + set_child_tid, + clear_child_tid, + root_vmar, + file_table, + fpu_context, + ); thread_table::add_thread(tid, thread.clone()); task::create_new_user_task(user_ctx, thread, thread_local) diff --git a/kernel/src/process/posix_thread/thread_local.rs b/kernel/src/process/posix_thread/thread_local.rs index 5d2ea69ea..5c4205b96 100644 --- a/kernel/src/process/posix_thread/thread_local.rs +++ b/kernel/src/process/posix_thread/thread_local.rs @@ -3,7 +3,7 @@ use core::cell::{Cell, Ref, RefCell, RefMut}; use aster_rights::Full; -use ostd::{mm::Vaddr, sync::RwArc, task::CurrentTask}; +use ostd::{cpu::context::FpuContext, mm::Vaddr, sync::RwArc, task::CurrentTask}; use super::RobustListHead; use crate::{fs::file_table::FileTable, process::signal::SigStack, vm::vmar::Vmar}; @@ -25,6 +25,10 @@ pub struct ThreadLocal { // Files. file_table: RefCell>>, + // User FPU context. + fpu_context: RefCell, + fpu_state: Cell, + // Signal. /// `ucontext` address for the signal handler. // FIXME: This field may be removed. For glibc applications with RESTORER flag set, the @@ -40,6 +44,7 @@ impl ThreadLocal { clear_child_tid: Vaddr, root_vmar: Vmar, file_table: RwArc, + fpu_context: FpuContext, ) -> Self { Self { set_child_tid: Cell::new(set_child_tid), @@ -49,6 +54,8 @@ impl ThreadLocal { file_table: RefCell::new(Some(file_table)), sig_context: Cell::new(None), sig_stack: RefCell::new(None), + fpu_context: RefCell::new(fpu_context), + fpu_state: Cell::new(FpuState::Unloaded), } } @@ -83,6 +90,96 @@ impl ThreadLocal { pub fn sig_stack(&self) -> &RefCell> { &self.sig_stack } + + pub fn fpu(&self) -> ThreadFpu<'_> { + ThreadFpu(self) + } +} + +/// The current state of `ThreadFpu`. +/// +/// - `Activated`: The FPU context is currently loaded onto the CPU and it must be loaded +/// while the associated task is running. If preemption occurs in between, the context switch +/// must load FPU context again. +/// - `Loaded`: The FPU context is currently loaded onto the CPU. It may or may not still +/// be loaded in CPU after a context switch. +/// - `Unloaded`: The FPU context is not currently loaded onto the CPU. +#[derive(Clone, Copy, Debug, PartialEq, Eq)] +enum FpuState { + Activated, + Loaded, + Unloaded, +} + +/// The FPU information for the _current_ thread. +/// +/// # Notes about kernel preemption +/// +/// All the methods of `ThreadFpu` assume that preemption will not occur. +/// This means that the FPU state will not change unexpectedly +/// (e.g., changing from `Loaded` to `Unloaded`). +/// +/// In the current architecture, this is always true because kernel +/// preemption was never implemented. More importantly, we cannot implement +/// kernel preemption without refactoring the `ThreadLocal` mechanism +/// because `ThreadLocal` cannot be accessed in interrupt handlers for +/// soundness reasons. But such access is necessary for the preempted +/// schedule. +/// +/// Therefore, we omit the preemption guards for better performance and +/// defer preemption considerations to future work. +pub struct ThreadFpu<'a>(&'a ThreadLocal); + +impl ThreadFpu<'_> { + pub fn activate(&self) { + match self.0.fpu_state.get() { + FpuState::Activated => return, + FpuState::Loaded => (), + FpuState::Unloaded => self.0.fpu_context.borrow_mut().load(), + } + self.0.fpu_state.set(FpuState::Activated); + } + + pub fn deactivate(&self) { + if self.0.fpu_state.get() == FpuState::Activated { + self.0.fpu_state.set(FpuState::Loaded); + } + } + + pub fn clone_context(&self) -> FpuContext { + match self.0.fpu_state.get() { + FpuState::Activated | FpuState::Loaded => { + let mut fpu_context = self.0.fpu_context.borrow_mut(); + fpu_context.save(); + fpu_context.clone() + } + FpuState::Unloaded => self.0.fpu_context.borrow().clone(), + } + } + + pub fn set_context(&self, context: FpuContext) { + let _ = self.0.fpu_context.replace(context); + self.0.fpu_state.set(FpuState::Unloaded); + } + + pub fn before_schedule(&self) { + match self.0.fpu_state.get() { + FpuState::Activated => { + self.0.fpu_context.borrow_mut().save(); + } + FpuState::Loaded => { + self.0.fpu_context.borrow_mut().save(); + self.0.fpu_state.set(FpuState::Unloaded); + } + FpuState::Unloaded => (), + } + } + + pub fn after_schedule(&self) { + if self.0.fpu_state.get() == FpuState::Activated { + self.0.fpu_context.borrow_mut().load(); + } + } } /// An immutable, shared reference to the file table in [`ThreadLocal`]. diff --git a/kernel/src/syscall/execve.rs b/kernel/src/syscall/execve.rs index 4170613cc..8ab5b6296 100644 --- a/kernel/src/syscall/execve.rs +++ b/kernel/src/syscall/execve.rs @@ -2,7 +2,7 @@ use aster_rights::WriteOp; use ostd::{ - cpu::context::{FpuState, GeneralRegs, UserContext}, + cpu::context::{FpuContext, GeneralRegs, UserContext}, user::UserContextApi, }; @@ -144,6 +144,9 @@ fn do_execve( *thread_local.robust_list().borrow_mut() = None; debug!("load elf in execve succeeds"); + // Reset FPU context + thread_local.fpu().set_context(FpuContext::new()); + let credentials = posix_thread.credentials_mut(); set_uid_from_elf(process, &credentials, &elf_file)?; set_gid_from_elf(process, &credentials, &elf_file)?; @@ -156,12 +159,6 @@ fn do_execve( // set cpu context to default *user_context.general_regs_mut() = GeneralRegs::default(); user_context.set_tls_pointer(0); - *user_context.fpu_state_mut() = FpuState::default(); - // FIXME: how to reset the FPU state correctly? Before returning to the user space, - // the kernel will call `handle_pending_signal`, which may update the CPU states so that - // when the kernel switches to the user mode, the control of the CPU will be handed over - // to the user-registered signal handlers. - user_context.fpu_state().restore(); // set new entry point user_context.set_instruction_pointer(elf_load_info.entry_point as _); debug!("entry_point: 0x{:x}", elf_load_info.entry_point); diff --git a/kernel/src/thread/mod.rs b/kernel/src/thread/mod.rs index 2aff60f53..487c25e67 100644 --- a/kernel/src/thread/mod.rs +++ b/kernel/src/thread/mod.rs @@ -22,6 +22,17 @@ pub mod work_queue; pub type Tid = u32; +fn pre_schedule_handler() { + let Some(task) = Task::current() else { + return; + }; + let Some(thread_local) = task.as_thread_local() else { + return; + }; + + thread_local.fpu().before_schedule(); +} + fn post_schedule_handler() { let task = Task::current().unwrap(); let Some(thread_local) = task.as_thread_local() else { @@ -32,9 +43,12 @@ fn post_schedule_handler() { if let Some(vmar) = root_vmar.as_ref() { vmar.vm_space().activate() } + + thread_local.fpu().after_schedule(); } pub(super) fn init() { + ostd::task::inject_pre_schedule_handler(pre_schedule_handler); ostd::task::inject_post_schedule_handler(post_schedule_handler); ostd::arch::trap::inject_user_page_fault_handler(exception::page_fault_handler); } diff --git a/kernel/src/thread/task.rs b/kernel/src/thread/task.rs index 7419732e4..3686e80ec 100644 --- a/kernel/src/thread/task.rs +++ b/kernel/src/thread/task.rs @@ -75,7 +75,9 @@ pub fn create_new_user_task( while !current_thread.is_exited() { // Execute the user code + ctx.thread_local.fpu().activate(); let return_reason = user_mode.execute(has_kernel_event_fn); + ctx.thread_local.fpu().deactivate(); // Handle user events let user_ctx = user_mode.context_mut(); diff --git a/ostd/src/arch/riscv/cpu/context.rs b/ostd/src/arch/riscv/cpu/context.rs index bace1550c..28dfce25b 100644 --- a/ostd/src/arch/riscv/cpu/context.rs +++ b/ostd/src/arch/riscv/cpu/context.rs @@ -14,13 +14,12 @@ use crate::{ user::{ReturnReason, UserContextApi, UserContextApiInternal}, }; -/// Userspace CPU context, including both general-purpose registers and FPU state. +/// Userspace CPU context, including general-purpose registers and exception information. #[derive(Clone, Copy, Debug)] #[repr(C)] pub struct UserContext { user_context: RawUserContext, trap: Trap, - fpu_state: FpuState, cpu_exception_info: Option, } @@ -81,7 +80,6 @@ impl Default for UserContext { UserContext { user_context: RawUserContext::default(), trap: Trap::Exception(Exception::Unknown), - fpu_state: FpuState, cpu_exception_info: None, } } @@ -120,16 +118,6 @@ impl UserContext { self.cpu_exception_info.take() } - /// Returns a reference to the FPU state. - pub fn fpu_state(&self) -> &FpuState { - &self.fpu_state - } - - /// Returns a mutable reference to the FPU state. - pub fn fpu_state_mut(&mut self) -> &mut FpuState { - &mut self.fpu_state - } - /// Sets thread-local storage pointer. pub fn set_tls_pointer(&mut self, tls: usize) { self.set_tp(tls) @@ -275,27 +263,32 @@ cpu_context_impl_getter_setter!( /// CPU exception. pub type CpuException = Exception; -/// The FPU state of user task. +/// The FPU context of user task. /// /// This could be used for saving both legacy and modern state format. -// FIXME: Implement FPU state on RISC-V platforms. -#[derive(Clone, Copy, Debug)] -pub struct FpuState; +// FIXME: Implement FPU context on RISC-V platforms. +#[derive(Clone, Copy, Debug, Default)] +pub struct FpuContext; -impl FpuState { - /// Saves CPU's current FPU state into this instance. - pub fn save(&self) { - todo!() +impl FpuContext { + /// Creates a new FPU context. + pub fn new() -> Self { + Self } - /// Restores CPU's FPU state from this instance. - pub fn restore(&self) { - todo!() - } -} - -impl Default for FpuState { - fn default() -> Self { - FpuState + /// Saves CPU's current FPU context to this instance, if needed. + pub fn save(&mut self) {} + + /// Loads CPU's FPU context from this instance, if needed. + pub fn load(&mut self) {} + + /// Returns the FPU context as a byte slice. + pub fn as_bytes(&self) -> &[u8] { + &[] + } + + /// Returns the FPU context as a mutable byte slice. + pub fn as_bytes_mut(&mut self) -> &mut [u8] { + &mut [] } } diff --git a/ostd/src/arch/x86/cpu/context/mod.rs b/ostd/src/arch/x86/cpu/context/mod.rs index 3fc0fb1f0..7bb27e98e 100644 --- a/ostd/src/arch/x86/cpu/context/mod.rs +++ b/ostd/src/arch/x86/cpu/context/mod.rs @@ -3,15 +3,12 @@ //! CPU execution context control. use alloc::boxed::Box; -use core::{ - arch::x86_64::{_fxrstor64, _fxsave64, _xrstor64, _xsave64}, - fmt::Debug, - sync::atomic::{AtomicBool, Ordering::Relaxed}, -}; +use core::arch::x86_64::{_fxrstor64, _fxsave64, _xrstor64, _xsave64}; use bitflags::bitflags; use cfg_if::cfg_if; use log::debug; +use ostd_pod::Pod; use spin::Once; use x86::bits64::segmentation::wrfsbase; use x86_64::registers::{ @@ -41,12 +38,11 @@ cfg_if! { pub use x86::cpuid; -/// Userspace CPU context, including both general-purpose registers and FPU state. +/// Userspace CPU context, including general-purpose registers and exception information. #[derive(Clone, Default, Debug)] #[repr(C)] pub struct UserContext { user_context: RawUserContext, - fpu_state: FpuState, exception: Option, } @@ -242,16 +238,6 @@ impl UserContext { self.exception.take() } - /// Returns a reference to the FPU state. - pub fn fpu_state(&self) -> &FpuState { - &self.fpu_state - } - - /// Returns a mutable reference to the FPU state. - pub fn fpu_state_mut(&mut self) -> &mut FpuState { - &mut self.fpu_state - } - /// Sets thread-local storage pointer. pub fn set_tls_pointer(&mut self, tls: usize) { self.set_fsbase(tls) @@ -506,19 +492,129 @@ cpu_context_impl_getter_setter!( [gsbase, set_gsbase] ); -/// The FPU state of user task. +/// The FPU context of user task. /// /// This could be used for saving both legacy and modern state format. #[derive(Debug)] -pub struct FpuState { - state_area: Box, +pub struct FpuContext { + xsave_area: Box, area_size: usize, - is_valid: AtomicBool, } -// The legacy SSE/MMX FPU state format (as saved by `FXSAVE` and restored by the `FXRSTOR` instructions). -#[repr(C, align(16))] -#[derive(Clone, Copy, Debug)] +impl FpuContext { + /// Creates a new FPU context. + pub fn new() -> Self { + let mut area_size = size_of::(); + if CPU_FEATURES.get().unwrap().has_xsave() { + area_size = area_size.max(*XSAVE_AREA_SIZE.get().unwrap()); + } + + Self { + xsave_area: Box::new(XSaveArea::new()), + area_size, + } + } + + /// Saves CPU's current FPU context to this instance. + pub fn save(&mut self) { + let mem_addr = self.as_bytes_mut().as_mut_ptr(); + + if CPU_FEATURES.get().unwrap().has_xsave() { + unsafe { _xsave64(mem_addr, XFEATURE_MASK_USER_RESTORE) }; + } else { + unsafe { _fxsave64(mem_addr) }; + } + + debug!("Save FPU context"); + } + + /// Loads CPU's FPU context from this instance. + pub fn load(&mut self) { + let mem_addr = self.as_bytes().as_ptr(); + + if CPU_FEATURES.get().unwrap().has_xsave() { + let rs_mask = XFEATURE_MASK_USER_RESTORE & XSTATE_MAX_FEATURES.get().unwrap(); + + unsafe { _xrstor64(mem_addr, rs_mask) }; + } else { + unsafe { _fxrstor64(mem_addr) }; + } + + debug!("Load FPU context"); + } + + /// Returns the FPU context as a byte slice. + pub fn as_bytes(&self) -> &[u8] { + &self.xsave_area.as_bytes()[..self.area_size] + } + + /// Returns the FPU context as a mutable byte slice. + pub fn as_bytes_mut(&mut self) -> &mut [u8] { + &mut self.xsave_area.as_bytes_mut()[..self.area_size] + } +} + +impl Default for FpuContext { + fn default() -> Self { + Self::new() + } +} + +impl Clone for FpuContext { + fn clone(&self) -> Self { + let mut xsave_area = Box::new(XSaveArea::new()); + xsave_area.fxsave_area = self.xsave_area.fxsave_area; + xsave_area.features = self.xsave_area.features; + xsave_area.compaction = self.xsave_area.compaction; + if self.area_size > size_of::() { + let len = self.area_size - size_of::() - 64; + xsave_area.extended_state_area[..len] + .copy_from_slice(&self.xsave_area.extended_state_area[..len]); + } + + Self { + xsave_area, + area_size: self.area_size, + } + } +} + +/// The modern FPU context format (as saved and restored by the `XSAVE` and `XRSTOR` instructions). +#[repr(C)] +#[repr(align(64))] +#[derive(Clone, Copy, Debug, Pod)] +struct XSaveArea { + fxsave_area: FxSaveArea, + features: u64, + compaction: u64, + reserved: [u64; 6], + extended_state_area: [u8; MAX_XSAVE_AREA_SIZE - size_of::() - 64], +} + +impl XSaveArea { + fn new() -> Self { + let features = if CPU_FEATURES.get().unwrap().has_xsave() { + XCr0::read().bits() & XSTATE_MAX_FEATURES.get().unwrap() + } else { + 0 + }; + + let mut xsave_area = Self::new_zeroed(); + // Set the initial values for the FPU context. Refer to Intel SDM, Table 11-1: + // "IA-32 and IntelĀ® 64 Processor States Following Power-up, Reset, or INIT (Contd.)". + xsave_area.fxsave_area.control = 0x037F; + xsave_area.fxsave_area.tag = 0xFFFF; + xsave_area.fxsave_area.mxcsr = 0x1F80; + xsave_area.features = features; + + xsave_area + } +} + +/// The legacy SSE/MMX FPU context format (as saved and restored by the `FXSAVE` and `FXRSTOR` instructions). +#[repr(C)] +#[repr(align(16))] +#[derive(Clone, Copy, Debug, Pod)] struct FxSaveArea { control: u16, // x87 FPU Control Word status: u16, // x87 FPU Status Word @@ -536,129 +632,6 @@ struct FxSaveArea { reserved: [u32; 12], // Software reserved } -/// The modern FPU state format (as saved by the `XSAVE`` and restored by the `XRSTOR` instructions). -#[repr(C, align(64))] -#[derive(Clone, Copy, Debug)] -struct XSaveArea { - fxsave_area: FxSaveArea, - features: u64, - compaction: u64, - reserved: [u64; 6], - extended_state_area: [u8; MAX_XSAVE_AREA_SIZE - size_of::() - 64], -} - -impl XSaveArea { - fn init() -> Box { - let features = if CPU_FEATURES.get().unwrap().has_xsave() { - XCr0::read().bits() & XSTATE_MAX_FEATURES.get().unwrap() - } else { - 0 - }; - - let mut xsave_area = Box::::new_uninit(); - let ptr = xsave_area.as_mut_ptr(); - // SAFETY: it's safe to initialize the XSaveArea field then return the instance. - unsafe { - core::ptr::write_bytes(ptr, 0, 1); - (*ptr).fxsave_area.control = 0x37F; - (*ptr).fxsave_area.mxcsr = 0x1F80; - (*ptr).features = features; - xsave_area.assume_init() - } - } -} - -impl FpuState { - /// Initializes a new instance. - pub fn init() -> Self { - let mut area_size = size_of::(); - if CPU_FEATURES.get().unwrap().has_xsave() { - area_size = area_size.max(*XSAVE_AREA_SIZE.get().unwrap()); - } - - Self { - state_area: XSaveArea::init(), - area_size, - is_valid: AtomicBool::new(true), - } - } - - /// Returns whether the instance can contains valid state. - pub fn is_valid(&self) -> bool { - self.is_valid.load(Relaxed) - } - - /// Save CPU's current FPU state into this instance. - pub fn save(&self) { - let mem_addr = &*self.state_area as *const _ as *mut u8; - - if CPU_FEATURES.get().unwrap().has_xsave() { - unsafe { _xsave64(mem_addr, XFEATURE_MASK_USER_RESTORE) }; - } else { - unsafe { _fxsave64(mem_addr) }; - } - - self.is_valid.store(true, Relaxed); - - debug!("Save FPU state"); - } - - /// Restores CPU's FPU state from this instance. - pub fn restore(&self) { - if !self.is_valid() { - return; - } - - let mem_addr = &*self.state_area as *const _ as *const u8; - - if CPU_FEATURES.get().unwrap().has_xsave() { - let rs_mask = XFEATURE_MASK_USER_RESTORE & XSTATE_MAX_FEATURES.get().unwrap(); - - unsafe { _xrstor64(mem_addr, rs_mask) }; - } else { - unsafe { _fxrstor64(mem_addr) }; - } - - self.is_valid.store(false, Relaxed); - - debug!("Restore FPU state"); - } - - /// Clears the state of the instance. - /// - /// This method does not reset the underlying buffer that contains the - /// FPU state; it only marks the buffer __invalid__. - pub fn clear(&self) { - self.is_valid.store(false, Relaxed); - } -} - -impl Clone for FpuState { - fn clone(&self) -> Self { - let mut state_area = XSaveArea::init(); - state_area.fxsave_area = self.state_area.fxsave_area; - state_area.features = self.state_area.features; - state_area.compaction = self.state_area.compaction; - if self.area_size > size_of::() { - let len = self.area_size - size_of::() - 64; - state_area.extended_state_area[..len] - .copy_from_slice(&self.state_area.extended_state_area[..len]); - } - - Self { - state_area, - area_size: self.area_size, - is_valid: AtomicBool::new(self.is_valid()), - } - } -} - -impl Default for FpuState { - fn default() -> Self { - Self::init() - } -} - /// The XSTATE features (user & supervisor) supported by the processor. static XSTATE_MAX_FEATURES: Once = Once::new(); @@ -690,7 +663,10 @@ pub(in crate::arch) fn enable_essential_features() { XSAVE_AREA_SIZE.call_once(|| { let cpuid = cpuid::CpuId::new(); - let size = cpuid.get_extended_state_info().unwrap().xsave_size() as usize; + let size = cpuid + .get_extended_state_info() + .unwrap() + .xsave_area_size_supported_features() as usize; debug_assert!(size <= MAX_XSAVE_AREA_SIZE); size }); diff --git a/ostd/src/task/mod.rs b/ostd/src/task/mod.rs index 3ea1ce2fc..6cdb6062c 100644 --- a/ostd/src/task/mod.rs +++ b/ostd/src/task/mod.rs @@ -30,8 +30,15 @@ pub use self::{ pub(crate) use crate::arch::task::{context_switch, TaskContext}; use crate::{cpu::context::UserContext, prelude::*, trap::in_interrupt_context}; +static PRE_SCHEDULE_HANDLER: Once = Once::new(); + static POST_SCHEDULE_HANDLER: Once = Once::new(); +/// Injects a handler to be executed before scheduling. +pub fn inject_pre_schedule_handler(handler: fn()) { + PRE_SCHEDULE_HANDLER.call_once(|| handler); +} + /// Injects a handler to be executed after scheduling. pub fn inject_post_schedule_handler(handler: fn()) { POST_SCHEDULE_HANDLER.call_once(|| handler); @@ -130,22 +137,6 @@ impl Task { None } } - - /// Saves the FPU state for user task. - pub fn save_fpu_state(&self) { - let Some(user_ctx) = self.user_ctx.as_ref() else { - return; - }; - user_ctx.fpu_state().save(); - } - - /// Restores the FPU state for user task. - pub fn restore_fpu_state(&self) { - let Some(user_ctx) = self.user_ctx.as_ref() else { - return; - }; - user_ctx.fpu_state().restore(); - } } /// Options to create or spawn a new task. @@ -215,8 +206,6 @@ impl TaskOptions { let current_task = Task::current() .expect("no current task, it should have current task in kernel task entry"); - current_task.restore_fpu_state(); - // SAFETY: The `func` field will only be accessed by the current task in the task // context, so the data won't be accessed concurrently. let task_func = unsafe { current_task.func.get() }; diff --git a/ostd/src/task/processor.rs b/ostd/src/task/processor.rs index 2c466c40a..bc574178a 100644 --- a/ostd/src/task/processor.rs +++ b/ostd/src/task/processor.rs @@ -3,7 +3,7 @@ use alloc::sync::Arc; use core::{ptr::NonNull, sync::atomic::Ordering}; -use super::{context_switch, Task, TaskContext, POST_SCHEDULE_HANDLER}; +use super::{context_switch, Task, TaskContext, POST_SCHEDULE_HANDLER, PRE_SCHEDULE_HANDLER}; use crate::{cpu_local_cell, trap::irq::DisabledLocalIrqGuard}; cpu_local_cell! { @@ -52,8 +52,6 @@ pub(super) fn switch_to_task(next_task: Arc) { // so its reference will be valid until `after_switching_to`. let current_task = unsafe { &*current_task_ptr }; - current_task.save_fpu_state(); - // Until `after_switching_to`, the task's context is alive and can be exclusively used. current_task.ctx.get() } else { @@ -86,13 +84,13 @@ pub(super) fn switch_to_task(next_task: Arc) { // SAFETY: The task is just switched back, `after_switching_to` hasn't been called yet. unsafe { after_switching_to() }; - - if let Some(current) = Task::current() { - current.restore_fpu_state(); - } } fn before_switching_to(next_task: &Task, irq_guard: &DisabledLocalIrqGuard) { + if let Some(handler) = PRE_SCHEDULE_HANDLER.get() { + handler(); + } + // Ensure that the mapping to the kernel stack is valid. next_task.kstack.flush_tlb(irq_guard);