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}