// SPDX-License-Identifier: MPL-2.0 //! User space. use trapframe::TrapFrame; use crate::{cpu::UserContext, prelude::*, task::Task, vm::VmSpace}; /// A user space. /// /// Each user space has a VM address space and allows a task to execute in /// user mode. pub struct UserSpace { /// vm space vm_space: VmSpace, /// cpu context before entering user space init_ctx: UserContext, } impl UserSpace { /// Creates a new instance. /// /// Each instance maintains a VM address space and the CPU state to enable /// execution in the user space. pub fn new(vm_space: VmSpace, init_ctx: UserContext) -> Self { Self { vm_space, init_ctx } } /// Returns the VM address space. pub fn vm_space(&self) -> &VmSpace { &self.vm_space } /// Returns the user mode that is bound to the current task and user space. /// /// See `UserMode` on how to use it to execute user code. /// /// # Panic /// /// This method is intended to only allow each task to have at most one /// instance of `UserMode` initiated. If this method is called again before /// the first instance for the current task is dropped, then the method /// panics. pub fn user_mode(&self) -> UserMode<'_> { todo!() } } /// Specific architectures need to implement this trait. This should only used in `UserMode` /// /// Only visible in aster-frame pub(crate) trait UserContextApiInternal { /// Starts executing in the user mode. fn execute(&mut self) -> UserEvent; /// Use the information inside CpuContext to build a trapframe fn as_trap_frame(&self) -> TrapFrame; } /// The common interface that every CPU architecture-specific `CpuContext` implements. pub trait UserContextApi { /// Get the trap number of this interrupt. fn trap_number(&self) -> usize; /// Get the trap error code of this interrupt. fn trap_error_code(&self) -> usize; /// Get number of syscall fn syscall_num(&self) -> usize; /// Get return value of syscall fn syscall_ret(&self) -> usize; /// Set return value of syscall fn set_syscall_ret(&mut self, ret: usize); /// Get syscall args fn syscall_args(&self) -> [usize; 6]; /// Set instruction pointer fn set_instruction_pointer(&mut self, ip: usize); /// Get instruction pointer fn instruction_pointer(&self) -> usize; /// Set stack pointer fn set_stack_pointer(&mut self, sp: usize); /// Get stack pointer fn stack_pointer(&self) -> usize; } /// Code execution in the user mode. /// /// This type enables executing the code in user space from a task in the kernel /// space safely. /// /// Here is a sample code on how to use `UserMode`. /// /// ```no_run /// use aster_frame::task::Task; /// /// let current = Task::current(); /// let user_space = current.user_space() /// .expect("the current task is associated with a user space"); /// let mut user_mode = user_space.user_mode(); /// loop { /// // Execute in the user space until some interesting user event occurs /// let user_event = user_mode.execute(); /// todo!("handle the user event, e.g., syscall"); /// } /// ``` pub struct UserMode<'a> { current: Arc, user_space: &'a Arc, context: UserContext, } // An instance of `UserMode` is bound to the current task. So it cannot be impl<'a> !Send for UserMode<'a> {} impl<'a> UserMode<'a> { pub fn new(user_space: &'a Arc) -> Self { Self { current: Task::current(), user_space, context: user_space.init_ctx, } } /// Starts executing in the user mode. Make sure current task is the task in `UserMode`. /// /// The method returns for one of three possible reasons indicated by `UserEvent`. /// 1. The user invokes a system call; /// 2. The user triggers an exception; /// 3. The user triggers a fault. /// /// After handling the user event and updating the user-mode CPU context, /// this method can be invoked again to go back to the user space. pub fn execute(&mut self) -> UserEvent { unsafe { self.user_space.vm_space().activate(); } debug_assert!(Arc::ptr_eq(&self.current, &Task::current())); self.context.execute() } /// Returns an immutable reference the user-mode CPU context. pub fn context(&self) -> &UserContext { &self.context } /// Returns a mutable reference the user-mode CPU context. pub fn context_mut(&mut self) -> &mut UserContext { &mut self.context } } #[derive(PartialEq, Eq, PartialOrd, Ord, Debug)] /// A user event is what brings back the control of the CPU back from /// the user space to the kernel space. /// /// Note that hardware interrupts are not considered user events as they /// are triggered by devices and not visible to user programs. /// To handle interrupts, one should register callback funtions for /// IRQ lines (`IrqLine`). pub enum UserEvent { Syscall, Exception, }