obkrnl/context/mod.rs
1pub use self::arc::*;
2pub use self::arch::*;
3pub use self::local::*;
4pub use self::setup::*;
5
6use crate::arch::ArchConfig;
7use crate::config::Config;
8use crate::proc::Thread;
9use crate::uma::Uma;
10use alloc::rc::Rc;
11use alloc::sync::Arc;
12use core::marker::PhantomData;
13use core::mem::offset_of;
14use core::pin::pin;
15use core::ptr::null;
16use core::sync::atomic::Ordering;
17
18mod arc;
19#[cfg_attr(target_arch = "aarch64", path = "aarch64.rs")]
20#[cfg_attr(target_arch = "x86_64", path = "x86_64.rs")]
21mod arch;
22mod local;
23mod setup;
24
25/// See `pcpu_init` on the Orbis for a reference.
26///
27/// # Safety
28/// - This function can be called only once per CPU.
29/// - `config` must be the same object for all context.
30/// - `arch` must be the same object for all context.
31/// - `cpu` must be unique and valid.
32/// - `setup` must return the same objects for all context.
33///
34/// # Reference offsets
35/// | Version | Offset |
36/// |---------|--------|
37/// |PS4 11.00|0x08DA70|
38pub unsafe fn run_with_context<T>(
39 config: Arc<Config>,
40 arch: Arc<ArchConfig>,
41 cpu: usize,
42 td: Arc<Thread>,
43 setup: impl FnOnce(&mut ContextSetup) -> T,
44 main: fn(T) -> !,
45) -> ! {
46 // We use a different mechanism here. The Orbis put all of pcpu at a global level but we put it
47 // on each CPU stack instead.
48 let mut cx = pin!(Context::new(
49 Base {
50 config: Arc::into_raw(config),
51 arch: Arc::into_raw(arch.clone()),
52 cpu,
53 thread: Arc::into_raw(td),
54 uma: null(),
55 },
56 &arch,
57 ));
58
59 unsafe { cx.as_mut().activate() };
60
61 // Prevent any code before and after this line to cross this line.
62 core::sync::atomic::fence(Ordering::AcqRel);
63
64 main(setup(&mut ContextSetup::new()));
65}
66
67/// # Interrupt safety
68/// This function can be called from interrupt handler.
69pub fn config() -> BorrowedArc<Config> {
70 // It does not matter if we are on a different CPU after we load the Context::arch because it is
71 // always the same for all CPU.
72 unsafe {
73 BorrowedArc::from_non_null(Context::load_static_ptr::<{ offset_of!(Base, config) }, _>())
74 }
75}
76
77/// # Interrupt safety
78/// This function can be called from interrupt handler.
79pub fn arch() -> BorrowedArc<ArchConfig> {
80 // It does not matter if we are on a different CPU after we load the Context::arch because it is
81 // always the same for all CPU.
82 unsafe {
83 BorrowedArc::from_non_null(Context::load_static_ptr::<{ offset_of!(Base, arch) }, _>())
84 }
85}
86
87/// # Interrupt safety
88/// This function is interrupt safe.
89pub fn current_thread() -> BorrowedArc<Thread> {
90 // It does not matter if we are on a different CPU after we load the Context::thread because it
91 // is going to be the same one since it represent the current thread.
92 unsafe {
93 BorrowedArc::from_non_null(Context::load_static_ptr::<{ current_thread_offset() }, _>())
94 }
95}
96
97pub const fn current_thread_offset() -> usize {
98 offset_of!(Base, thread)
99}
100
101/// Returns [`None`] if called from context setup function.
102///
103/// # Interrupt safety
104/// This function can be called from interrupt handler.
105pub fn uma() -> Option<BorrowedArc<Uma>> {
106 // It does not matter if we are on a different CPU after we load the Context::uma because it is
107 // always the same for all CPU.
108 unsafe { BorrowedArc::new(Context::load_ptr::<{ offset_of!(Base, uma) }, _>()) }
109}
110
111/// Pin the calling thread to one CPU.
112///
113/// This thread will never switch to a different CPU until the returned [`PinnedContext`] is dropped
114/// and it is not allowed to sleep.
115///
116/// See `critical_enter` and `critical_exit` on the PS4 for a reference. Beware that our
117/// implementation a bit different. The PS4 **allow the thread to sleep but we don't**.
118pub fn pin_cpu() -> PinnedContext {
119 let td = current_thread();
120
121 // Prevent all operations after this to get executed before this line. See
122 // https://github.com/rust-lang/rust/issues/130655#issuecomment-2365189317 for the explanation.
123 unsafe { td.active_pins().fetch_add(1, Ordering::Acquire) };
124
125 PinnedContext {
126 td,
127 phantom: PhantomData,
128 }
129}
130
131/// Implementation of `pcpu` structure.
132///
133/// Access to this structure must be done by **atomic reading or writing its field directly**. It is
134/// not safe to have a temporary a pointer or reference to this struct or its field because the CPU
135/// might get interrupted, which mean it is possible for the next instruction to get executed on
136/// a different CPU if the interrupt cause the CPU to switch the task.
137///
138/// The activation of this struct is a minimum requirements for a new CPU to call most of the other
139/// functions. The new CPU should call [`run_with_context()`] as soon as possible. We don't make the
140/// functions that require this context as `unsafe` nor make it check for the context because it
141/// will be (almost) all of it. So we impose this requirement on a function that setup a CPU
142/// instead.
143///
144/// Beware for any type that implement [`Drop`] because it may access the CPU context. For maximum
145/// safety the CPU setup function **must not cause any value of the kernel type to drop before
146/// context is activated**. It is safe to drop values of Rust core type (e.g. `String`) **only on a
147/// main CPU** because the only kernel functions it can call into is either stage 1 allocator or
148/// panic handler, both of them does not require a CPU context.
149#[repr(C)]
150struct Base {
151 config: *const Config,
152 arch: *const ArchConfig,
153 cpu: usize, // pc_cpuid
154 thread: *const Thread, // pc_curthread
155 uma: *const Uma,
156}
157
158impl Drop for Base {
159 fn drop(&mut self) {
160 panic!("dropping Context can cause a bug so it is not supported");
161 }
162}
163
164/// RAII struct to pin the current thread to a CPU.
165///
166/// This struct must not implement [`Send`] and [`Sync`].
167pub struct PinnedContext {
168 td: BorrowedArc<Thread>,
169 phantom: PhantomData<Rc<()>>, // Make sure we are !Send and !Sync.
170}
171
172impl PinnedContext {
173 /// See [`CpuLocal`] for a safe alternative if you want to store per-CPU value.
174 ///
175 /// # Safety
176 /// Anything that derive from the returned value will invalid when this [`PinnedContext`]
177 /// dropped.
178 pub unsafe fn cpu(&self) -> usize {
179 unsafe { Context::load_volatile_usize::<{ offset_of!(Base, cpu) }>() }
180 }
181}
182
183impl Drop for PinnedContext {
184 fn drop(&mut self) {
185 // Prevent all operations before this to get executed after this line. See
186 // https://github.com/rust-lang/rust/issues/130655#issuecomment-2365189317 for the explanation.
187 unsafe { self.td.active_pins().fetch_sub(1, Ordering::Release) };
188
189 // TODO: Implement td_owepreempt.
190 }
191}