ostd/
user.rs

1// SPDX-License-Identifier: MPL-2.0
2
3//! User mode.
4
5use crate::arch::{cpu::context::UserContext, trap::TrapFrame};
6
7/// Specific architectures need to implement this trait. This should only used in [`UserMode`]
8///
9/// Only visible in `ostd`.
10pub(crate) trait UserContextApiInternal {
11    /// Starts executing in the user mode.
12    fn execute<F>(&mut self, has_kernel_event: F) -> ReturnReason
13    where
14        F: FnMut() -> bool;
15
16    /// Uses the information inside CpuContext to build a trapframe
17    fn as_trap_frame(&self) -> TrapFrame;
18}
19
20/// The common interface that every CPU architecture-specific [`UserContext`] implements.
21pub trait UserContextApi {
22    /// Gets the trap number of this interrupt.
23    fn trap_number(&self) -> usize;
24
25    /// Gets the trap error code of this interrupt.
26    fn trap_error_code(&self) -> usize;
27
28    /// Sets the instruction pointer
29    fn set_instruction_pointer(&mut self, ip: usize);
30
31    /// Gets the instruction pointer
32    fn instruction_pointer(&self) -> usize;
33
34    /// Sets the stack pointer
35    fn set_stack_pointer(&mut self, sp: usize);
36
37    /// Gets the stack pointer
38    fn stack_pointer(&self) -> usize;
39}
40
41/// Code execution in the user mode.
42///
43/// This type enables executing the code in user space from a task in the kernel
44/// space safely.
45///
46/// Here is a sample code on how to use `UserMode`.
47///  
48/// ```no_run
49/// use ostd::task::Task;
50///
51/// let current = Task::current();
52/// let user_ctx = current.user_ctx()
53///     .expect("the current task is not associated with a user context");
54/// let mut user_mode = UserMode::new(UserContext::clone(user_ctx));
55/// loop {
56///     // Execute in the user space until some interesting events occur.
57///     // Note: users should activate a suitable `VmSpace` before to support
58///     // user-mode execution.
59///     let return_reason = user_mode.execute(|| false);
60///     todo!("handle the event, e.g., syscall");
61/// }
62/// ```
63pub struct UserMode {
64    context: UserContext,
65}
66
67// An instance of `UserMode` is bound to the current task. So it must not be sent to other tasks.
68impl !Send for UserMode {}
69// Note that implementing `!Sync` is unnecessary
70// because entering the user space via `UserMode` requires taking a mutable reference.
71
72impl UserMode {
73    /// Creates a new `UserMode`.
74    pub fn new(context: UserContext) -> Self {
75        Self { context }
76    }
77
78    /// Starts executing in the user mode. Make sure current task is the task in `UserMode`.
79    ///
80    /// The method returns for one of three possible reasons indicated by [`ReturnReason`].
81    /// 1. A system call is issued by the user space;
82    /// 2. A CPU exception is triggered by the user space;
83    /// 3. A kernel event is pending, as indicated by the given closure.
84    ///
85    /// After handling whatever user or kernel events that
86    /// cause the method to return
87    /// and updating the user-mode CPU context,
88    /// this method can be invoked again to go back to the user space.
89    #[track_caller]
90    pub fn execute<F>(&mut self, has_kernel_event: F) -> ReturnReason
91    where
92        F: FnMut() -> bool,
93    {
94        crate::task::atomic_mode::might_sleep();
95        self.context.execute(has_kernel_event)
96    }
97
98    /// Returns an immutable reference the user-mode CPU context.
99    pub fn context(&self) -> &UserContext {
100        &self.context
101    }
102
103    /// Returns a mutable reference the user-mode CPU context.
104    pub fn context_mut(&mut self) -> &mut UserContext {
105        &mut self.context
106    }
107}
108
109#[derive(PartialEq, Eq, Debug)]
110/// A reason as to why the control of the CPU is returned from
111/// the user space to the kernel.
112pub enum ReturnReason {
113    /// A system call is issued by the user space.
114    UserSyscall,
115    /// A CPU exception is triggered by the user space.
116    UserException,
117    /// A kernel event is pending
118    KernelEvent,
119}