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}