Files
asterinas/ostd/src/user.rs

166 lines
4.9 KiB
Rust

// SPDX-License-Identifier: MPL-2.0
#![allow(dead_code)]
//! User space.
use trapframe::TrapFrame;
use crate::{cpu::UserContext, mm::VmSpace, prelude::*, task::Task};
/// A user space.
///
/// Each user space has a VM address space and allows a task to execute in
/// user mode.
#[derive(Debug)]
pub struct UserSpace {
/// vm space
vm_space: Arc<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: Arc<VmSpace>, init_ctx: UserContext) -> Self {
Self { vm_space, init_ctx }
}
/// Returns the VM address space.
pub fn vm_space(&self) -> &Arc<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.
///
/// # Panics
///
/// 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 `ostd`.
pub(crate) trait UserContextApiInternal {
/// Starts executing in the user mode.
fn execute<F>(&mut self, has_kernel_event: F) -> ReturnReason
where
F: FnMut() -> bool;
/// Uses the information inside CpuContext to build a trapframe
fn as_trap_frame(&self) -> TrapFrame;
}
/// The common interface that every CPU architecture-specific [`UserContext`] implements.
pub trait UserContextApi {
/// Gets the trap number of this interrupt.
fn trap_number(&self) -> usize;
/// Gets the trap error code of this interrupt.
fn trap_error_code(&self) -> usize;
/// Sets the instruction pointer
fn set_instruction_pointer(&mut self, ip: usize);
/// Gets the instruction pointer
fn instruction_pointer(&self) -> usize;
/// Sets the stack pointer
fn set_stack_pointer(&mut self, sp: usize);
/// Gets the 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 ostd::task::Task;
///
/// let current = Task::current();
/// let user_space = current.user_space()
/// .expect("the current task is not associated with a user space");
/// let mut user_mode = user_space.user_mode();
/// loop {
/// // Execute in the user space until some interesting events occur.
/// let return_reason = user_mode.execute(|| false);
/// todo!("handle the event, e.g., syscall");
/// }
/// ```
pub struct UserMode<'a> {
current: Arc<Task>,
user_space: &'a Arc<UserSpace>,
context: UserContext,
}
// An instance of `UserMode` is bound to the current task. So it cannot be [`Send`].
impl<'a> !Send for UserMode<'a> {}
impl<'a> UserMode<'a> {
/// Creates a new `UserMode`.
pub fn new(user_space: &'a Arc<UserSpace>) -> Self {
Self {
current: Task::current().unwrap(),
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 [`ReturnReason`].
/// 1. A system call is issued by the user space;
/// 2. A CPU exception is triggered by the user space;
/// 3. A kernel event is pending, as indicated by the given closure.
///
/// After handling whatever user or kernel events that
/// cause the method to return
/// and updating the user-mode CPU context,
/// this method can be invoked again to go back to the user space.
pub fn execute<F>(&mut self, has_kernel_event: F) -> ReturnReason
where
F: FnMut() -> bool,
{
debug_assert!(Arc::ptr_eq(&self.current, &Task::current().unwrap()));
self.context.execute(has_kernel_event)
}
/// 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 reason as to why the control of the CPU is returned from
/// the user space to the kernel.
pub enum ReturnReason {
/// A system call is issued by the user space.
UserSyscall,
/// A CPU exception is triggered by the user space.
UserException,
/// A kernel event is pending
KernelEvent,
}