1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164
// 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.
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) -> &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 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
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(),
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()));
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,
}