| // Copyright 2025 The Pigweed Authors |
| // |
| // Licensed under the Apache License, Version 2.0 (the "License"); you may not |
| // use this file except in compliance with the License. You may obtain a copy of |
| // the License at |
| // |
| // https://www.apache.org/licenses/LICENSE-2.0 |
| // |
| // Unless required by applicable law or agreed to in writing, software |
| // distributed under the License is distributed on an "AS IS" BASIS, WITHOUT |
| // WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the |
| // License for the specific language governing permissions and limitations under |
| // the License. |
| |
| use core::cell::UnsafeCell; |
| use core::mem::MaybeUninit; |
| use core::ptr::NonNull; |
| use core::sync::atomic::Ordering; |
| |
| use exit_status::ExitStatus; |
| #[cfg(not(feature = "user_space"))] |
| use foreign_box::ForeignBox; |
| #[cfg(feature = "user_space")] |
| use foreign_box::{ForeignBox, ForeignRc}; |
| use list::*; |
| use pw_atomic::{AtomicAdd, AtomicSub, AtomicZero}; |
| use pw_log::info; |
| use pw_status::Result; |
| use pw_time_core::Instant; |
| |
| use crate::Kernel; |
| #[cfg(feature = "user_space")] |
| use crate::object::KernelObject; |
| use crate::scheduler::algorithm::SchedulerAlgorithmThreadState; |
| use crate::scheduler::process::ProcessHandle; |
| use crate::scheduler::{JoinResult, Priority, SchedulerState, TryJoinResult, WaitQueue, WaitType}; |
| use crate::sync::event::{Event, EventConfig, EventSignaler}; |
| use crate::sync::spinlock::SpinLockGuard; |
| |
| /// The memory backing a thread's stack before it has been started. |
| /// |
| /// Stacks are aligned to 8 bytes for broad ABI compatibility. |
| /// |
| /// After a thread has been started, ownership of its stack's memory is (from |
| /// the Rust Abstract Machine (AM) perspective) relinquished, and so the type we |
| /// use to represent that memory is irrelevant. |
| /// |
| /// However, while we are initializing a thread in preparation for starting it, |
| /// we are operating on that memory as a normal Rust variable, and so its type |
| /// is important. |
| /// |
| /// Using `MaybeUninit<u8>` instead of `u8` is important for two reasons: |
| /// - It ensures that it is sound to write values which are not entirely |
| /// initialized (e.g., which contain padding bytes). |
| /// - It ensures that pointers written to the stack [retain |
| /// provenance][provenance]. |
| /// |
| /// [provenance]: https://github.com/rust-lang/unsafe-code-guidelines/issues/286#issuecomment-2837585644 |
| #[repr(align(8))] |
| pub struct StackStorage<const N: usize> { |
| pub stack: [MaybeUninit<u8>; N], |
| } |
| |
| pub trait StackStorageExt { |
| const ZEROED: Self; |
| } |
| |
| impl<const N: usize> StackStorageExt for StackStorage<N> { |
| const ZEROED: StackStorage<N> = StackStorage { |
| stack: [MaybeUninit::new(0); N], |
| }; |
| } |
| |
| #[derive(Clone, Copy)] |
| pub struct Stack { |
| // Starting (lowest) address of the stack. Inclusive. |
| start: *const MaybeUninit<u8>, |
| |
| // Ending (highest) address of the stack. Exclusive. |
| end: *const MaybeUninit<u8>, |
| } |
| |
| #[allow(dead_code)] |
| impl Stack { |
| #[must_use] |
| pub const fn from_slice(slice: &[MaybeUninit<u8>]) -> Self { |
| let start: *const MaybeUninit<u8> = slice.as_ptr(); |
| // Safety: offset based on known size of slice. |
| let end = unsafe { start.add(slice.len()) }; |
| Self { start, end } |
| } |
| |
| #[must_use] |
| pub const fn new() -> Self { |
| Self { |
| start: core::ptr::null(), |
| end: core::ptr::null(), |
| } |
| } |
| |
| #[must_use] |
| pub fn start(&self) -> *const MaybeUninit<u8> { |
| self.start |
| } |
| |
| #[must_use] |
| pub fn end(&self) -> *const MaybeUninit<u8> { |
| self.end |
| } |
| |
| /// # Safety |
| /// Caller must ensure exclusive mutable access to underlying data |
| #[must_use] |
| pub unsafe fn end_mut(&self) -> *mut MaybeUninit<u8> { |
| self.end as *mut MaybeUninit<u8> |
| } |
| |
| #[must_use] |
| pub fn contains(&self, ptr: *const MaybeUninit<u8>) -> bool { |
| ptr >= self.start && ptr < self.end |
| } |
| |
| #[must_use] |
| pub fn aligned_stack_allocation_mut<T: Sized>( |
| sp: *mut MaybeUninit<u8>, |
| alignment: usize, |
| ) -> *mut T { |
| let sp = sp.wrapping_byte_sub(size_of::<T>()); |
| let offset = sp.align_offset(alignment); |
| if offset > 0 { |
| sp.wrapping_byte_sub(alignment - offset).cast() |
| } else { |
| sp.cast() |
| } |
| } |
| |
| pub fn aligned_stack_allocation<T: Sized>( |
| sp: *mut MaybeUninit<u8>, |
| alignment: usize, |
| ) -> *const T { |
| Self::aligned_stack_allocation_mut::<*mut T>(sp, alignment).cast() |
| } |
| |
| /// Initialize the stack for thread execution. |
| /// |
| /// Initializes the stack to a known pattern to avoid leaking data between |
| /// thread invocations as well as to provide a signature for calculating |
| /// high water stack usage. |
| pub fn initialize(&self) { |
| let mut ptr = self.start as *mut u32; |
| let end = self.end as *mut u32; |
| |
| pw_assert::assert!(ptr.is_aligned(), "Stack start is not aligned"); |
| pw_assert::assert!(end.is_aligned(), "Stack end is not aligned"); |
| |
| while ptr < end { |
| // SAFETY: `ptr` is always aligned to 4 bytes and is contained within |
| // the specified stack memory region. Additionally, only writes |
| // are performed for the MaybeUninit constraint is not violated. |
| unsafe { |
| ptr.write_volatile(magic_values::UNUSED_STACK_PATTERN); |
| ptr = ptr.add(1); |
| } |
| } |
| } |
| } |
| |
| /// Runtime state of a Thread. |
| // TODO: want to name this ThreadState, but collides with ArchThreadstate |
| #[derive(Copy, Clone, PartialEq)] |
| #[repr(u8)] |
| pub enum State { |
| /// Thread has been created but not initialized. |
| New = 0, |
| |
| /// Thread has been initialized and added to its parent process but has not |
| /// been added to the scheduler. |
| Initial = 1, |
| |
| /// Thread is ready to run and owned by the scheduling algorithm. |
| Ready = 2, |
| |
| /// Thread is currently running on a CPU core. |
| Running = 3, |
| |
| /// Thread has been successfully terminated and is waiting to be joined. |
| Terminated = 4, |
| |
| /// Thread has been joined, removed from its parent process, and no longer |
| /// participates in the system scheduler. |
| Joined = 5, |
| |
| /// Thread is interruptibly waiting in a [`WaitQueue`]. |
| WaitingInterruptible = 6, |
| |
| /// Thread is non-interruptibly waiting in a [`WaitQueue`]. |
| WaitingNonInterruptible = 7, |
| } |
| |
| impl State { |
| #[must_use] |
| pub fn is_waiting(&self) -> bool { |
| matches!( |
| self, |
| Self::WaitingInterruptible | Self::WaitingNonInterruptible |
| ) |
| } |
| } |
| |
| // TODO: use From or Into trait (unclear how to do it with 'static str) |
| pub(super) fn to_string(s: State) -> &'static str { |
| match s { |
| State::New => "New", |
| State::Initial => "Initial", |
| State::Ready => "Ready", |
| State::Running => "Running", |
| State::Terminated => "Terminated", |
| State::Joined => "Joined", |
| State::WaitingInterruptible => "WaitingInterruptible", |
| State::WaitingNonInterruptible => "WaitingNonInterruptible", |
| } |
| } |
| |
| pub trait ThreadState: 'static + Sized { |
| const NEW: Self; |
| |
| // TODO: Maybe have a `MemoryConfigContext` super-trait of `ThreadState`? |
| type MemoryConfig: memory_config::MemoryConfig; |
| |
| /// Initialize the default frame of a kernel thread |
| /// |
| /// Arranges for the thread to start at `initial_function` with arguments |
| /// passed in the first two argument slots. The stack pointer of the thread |
| /// is set to the top of the kernel stack. |
| /// |
| /// # Safety |
| /// Caller guarantees that the `memory_config` pointer remains valid for the |
| /// lifetime of the thread. |
| /// |
| /// # Implementation Note |
| /// The implementation is responsible for ensuring that `initial_function` |
| /// is called with interrupts enabled and without the scheduler lock held. |
| /// This call must initialize the `ThreadLocalState` returned by |
| /// `thread_local_state()`. |
| unsafe fn initialize_kernel_state( |
| &mut self, |
| kernel_stack: Stack, |
| memory_config: *const Self::MemoryConfig, |
| initial_function: extern "C" fn(usize, usize, usize), |
| args: (usize, usize, usize), |
| ); |
| |
| /// Initialize the default frame of a user thread |
| /// |
| /// Arranges for the thread to start at `initial_function` with arguments |
| /// passed in the first two argument slots |
| /// |
| /// # Safety |
| /// Caller guarantees that the `memory_config` pointer remains valid for the |
| /// lifetime of the thread. |
| /// |
| /// # Implementation Note |
| /// The implementation is responsible for ensuring that `initial_function` |
| /// is called with interrupts enabled, memory_config enabled, non-privileged |
| /// mode, and without the scheduler lock held. |
| /// This call must initialize the `ThreadLocalState` returned by |
| /// `thread_local_state()`. |
| #[cfg(feature = "user_space")] |
| unsafe fn initialize_user_state( |
| &mut self, |
| kernel_stack: Stack, |
| memory_config: *const Self::MemoryConfig, |
| initial_sp: usize, |
| initial_pc: usize, |
| args: (usize, usize, usize), |
| ) -> Result<()>; |
| } |
| |
| pub enum ThreadOwner<K: Kernel> { |
| None, |
| Scheduler, |
| WaitQueue { |
| queue: NonNull<WaitQueue<K>>, |
| wait_type: WaitType, |
| }, |
| } |
| |
| /// A reference counted reference to a [`Thread`]. |
| /// |
| /// A `ThreadHandle` can has a limited set of APIs that are safe to call without |
| /// holding the scheduler lock. |
| pub struct ThreadHandle<K: Kernel> { |
| pub(crate) thread: NonNull<Thread<K>>, |
| kernel: K, |
| } |
| |
| // Safety: ThreadHandle is a handle to a reference counted thread. All operations |
| // on the thread are protected by the scheduler lock or atomic operations. |
| unsafe impl<K: Kernel> Send for ThreadHandle<K> {} |
| unsafe impl<K: Kernel> Sync for ThreadHandle<K> {} |
| |
| impl<K: Kernel> ThreadHandle<K> { |
| /// Join the referenced thread. |
| /// |
| /// Waits until the all other references to the thread are dropped and the |
| /// thread terminates. Returns a `ForeignBox<Thread<K>>` which can be used |
| /// to restart the thread. |
| pub fn join(self, kernel: K) -> Result<(ForeignBox<Thread<K>>, ExitStatus)> { |
| match self.join_until(kernel, Instant::<K::Clock>::MAX) { |
| JoinResult::Joined(thread, status) => Ok((thread, status)), |
| JoinResult::Err { error, .. } => Err(error), |
| } |
| } |
| |
| pub fn try_join(self, kernel: K) -> crate::scheduler::TryJoinResult<K> { |
| let sched = kernel.get_scheduler().lock(kernel); |
| let (_sched, res) = self.try_join_locked(kernel, sched); |
| res |
| } |
| |
| pub fn try_join_locked<'a>( |
| self, |
| kernel: K, |
| sched: SpinLockGuard<'a, K, SchedulerState<K>>, |
| ) -> ( |
| SpinLockGuard<'a, K, SchedulerState<K>>, |
| crate::scheduler::TryJoinResult<K>, |
| ) { |
| sched.thread_try_join(kernel, self, None) |
| } |
| |
| /// Join the referenced thread with a deadline |
| /// |
| /// Waits until the all other references to the thread are dropped and the |
| /// thread terminates. |
| /// |
| /// Returns: |
| /// - `Ok(thread)`: Success. `thread` can be used to restart the thread. |
| /// - `Err(Error::DeadlineExceeded: The thread did not enter a joinable state |
| /// before `deadline` was passed. |
| pub fn join_until(mut self, kernel: K, deadline: Instant<K::Clock>) -> JoinResult<K> { |
| let join_event = Event::new(kernel, EventConfig::ManualReset); |
| loop { |
| let (_, res) = kernel.get_scheduler().lock(kernel).thread_try_join( |
| kernel, |
| self, |
| Some(join_event.get_signaler()), |
| ); |
| match res { |
| TryJoinResult::Err { |
| error: e, |
| thread: thread_handle, |
| } => { |
| return JoinResult::Err { |
| error: e, |
| thread: thread_handle, |
| }; |
| } |
| TryJoinResult::Joined(thread_box, status) => { |
| return JoinResult::Joined(thread_box, status); |
| } |
| TryJoinResult::Wait(thread_handle) => self = thread_handle, |
| }; |
| |
| if let Err(e) = join_event.wait_until(deadline) { |
| kernel |
| .get_scheduler() |
| .lock(kernel) |
| .thread_cancel_try_join(&mut self); |
| |
| return JoinResult::Err { |
| error: e, |
| thread: self, |
| }; |
| } |
| } |
| } |
| |
| /// Request termination of the thread. |
| /// |
| /// Asynchronously requests the referenced thread to terminate. While in the terminating |
| /// state: |
| /// - The thread's `terminating` field will be set to `true`. |
| /// - Any active interruptible waits will be canceled with `Error::Cancelled`. |
| /// - Any new interruptible wait will immediately return `Error::Cancelled`. |
| /// - All non-interruptible waits will work as normal. |
| /// |
| /// This is an ASYNCHRONOUS operation. The thread is marked as terminating, |
| /// but it may not exit immediately. |
| /// |
| /// To wait for the thread to terminate, call [`ThreadHandle::join()`] or |
| /// [`ThreadHandle::join_until()`]. |
| pub fn terminate(&mut self, kernel: K, status: ExitStatus) -> Result<()> { |
| kernel |
| .get_scheduler() |
| .lock(kernel) |
| .thread_terminate(self, status) |
| } |
| |
| /// Returns the current state of the thread. |
| pub fn get_state(&self, kernel: K) -> State { |
| kernel.get_scheduler().lock(kernel).thread_get_state(self) |
| } |
| |
| /// Returns true if the thread is in the terminating state. |
| /// |
| /// Note: This is a parallel state to the state returned by [`ThreadHandle::is_terminating()`]. |
| pub fn is_terminating(&self, kernel: K) -> bool { |
| kernel |
| .get_scheduler() |
| .lock(kernel) |
| .thread_is_terminating(self) |
| } |
| } |
| |
| impl<K: Kernel> Clone for ThreadHandle<K> { |
| fn clone(&self) -> Self { |
| unsafe { |
| self.thread |
| .as_ref() |
| .ref_count |
| .fetch_add(1, Ordering::Acquire); |
| } |
| |
| Self { |
| thread: self.thread, |
| kernel: self.kernel, |
| } |
| } |
| } |
| |
| impl<K: Kernel> Drop for ThreadHandle<K> { |
| fn drop(&mut self) { |
| unsafe { |
| let prev_value = self |
| .thread |
| .as_ref() |
| .ref_count |
| .fetch_sub(1, Ordering::Release); |
| |
| // If this ref was one of two outstanding references to the thread, |
| // the other reference may be attempting to join. Let the scheduler |
| // notify the join request if it is outstanding. |
| if prev_value == 2 { |
| self.kernel |
| .get_scheduler() |
| .lock(self.kernel) |
| .thread_signal_join(self); |
| } |
| }; |
| } |
| } |
| |
| pub struct Thread<K: Kernel> { |
| // List of threads in a given process. |
| pub process_link: Link, |
| |
| // Active state link (run queue, wait queue, etc) |
| pub active_link: Link, |
| |
| process: Option<ProcessHandle<K>>, |
| |
| pub(super) state: State, |
| pub(super) stack: Stack, |
| |
| // Architecturally specific thread state, saved on context switch |
| pub arch_thread_state: UnsafeCell<K::ThreadState>, |
| |
| pub(super) owner: ThreadOwner<K>, |
| pub(super) ref_count: K::AtomicUsize, |
| pub(super) terminating: bool, |
| pub(super) join_event: Option<EventSignaler<K>>, |
| pub(super) exit_status: Option<ExitStatus>, |
| |
| // TODO - konkers: allow this to be tokenized. |
| pub name: &'static str, |
| |
| /// The state for the scheduler algorithm. |
| pub algorithm_state: SchedulerAlgorithmThreadState, |
| |
| #[cfg(feature = "user_space")] |
| pub object: Option<foreign_box::ForeignRc<K::AtomicUsize, crate::object::ThreadObject<K>>>, |
| } |
| |
| // SAFETY: Thread internal state is protected by the scheduler lock or atomic operations. |
| unsafe impl<K: Kernel> Send for Thread<K> {} |
| unsafe impl<K: Kernel> Sync for Thread<K> {} |
| |
| list::define_adapter!(pub ThreadListAdapter<K: Kernel> => Thread<K>::active_link); |
| |
| impl<K: Kernel> Thread<K> { |
| pub(super) fn take_process(&mut self) -> ProcessHandle<K> { |
| let Some(process) = self.process.take() else { |
| pw_assert::panic!("Thread does not have a process"); |
| }; |
| process |
| } |
| |
| pub fn get_exit_status(&self) -> Option<ExitStatus> { |
| self.exit_status |
| } |
| |
| pub fn is_terminating(&self) -> bool { |
| self.terminating |
| } |
| |
| pub fn set_stack(&mut self, stack: Stack) { |
| pw_assert::assert!(matches!(self.state, State::New | State::Joined)); |
| self.stack = stack; |
| } |
| |
| // Create an empty, uninitialized thread |
| #[must_use] |
| pub const fn new(name: &'static str, priority: Priority, stack: Stack) -> Self { |
| Thread { |
| process_link: Link::new(), |
| active_link: Link::new(), |
| process: None, |
| state: State::New, |
| arch_thread_state: UnsafeCell::new(K::ThreadState::NEW), |
| owner: ThreadOwner::None, |
| stack, |
| ref_count: K::AtomicUsize::ZERO, |
| terminating: false, |
| join_event: None, |
| exit_status: None, |
| name, |
| algorithm_state: SchedulerAlgorithmThreadState::new(priority), |
| #[cfg(feature = "user_space")] |
| object: None, |
| } |
| } |
| |
| #[cfg(feature = "user_space")] |
| pub fn get_object( |
| &self, |
| kernel: K, |
| handle: u32, |
| ) -> Option<ForeignRc<K::AtomicUsize, dyn KernelObject<K>>> { |
| // SAFETY: `self.process` will always outlive `self`. |
| unsafe { |
| self.process |
| .as_ref()? |
| .process |
| .as_ref() |
| .get_object(kernel, handle) |
| } |
| } |
| |
| pub(super) extern "C" fn trampoline<A1: ThreadArg>( |
| entry_point: usize, |
| arg0: usize, |
| arg1: usize, |
| ) { |
| let entry_point = core::ptr::with_exposed_provenance::<()>(entry_point); |
| // SAFETY: This function is only ever passed to the |
| // architecture-specific call to `initialize_frame` below. It is |
| // never called directly. In `initialize_frame`, the first argument |
| // is `entry_point as usize`. `entry_point` is a `fn(usize)`. Thus, |
| // this transmute preserves validity, and the preceding |
| // `with_exposed_provenance` ensures that the resulting `fn(usize)` |
| // has valid provenance for its referent. |
| let entry_point: fn(K, A1) = unsafe { core::mem::transmute(entry_point) }; |
| let kernel = unsafe { K::from_usize(arg0) }; |
| let arg1 = unsafe { A1::from_usize(arg1) }; |
| entry_point(kernel, arg1); |
| } |
| |
| pub fn re_initialize_kernel_thread<A: ThreadArg>( |
| &mut self, |
| kernel: K, |
| entry_point: fn(K, A), |
| arg: A, |
| ) { |
| kernel |
| .get_scheduler() |
| .lock(kernel) |
| .thread_reinitialize_kernel(kernel, self, entry_point, arg) |
| } |
| |
| pub fn initialize_kernel_thread<A: ThreadArg>( |
| &mut self, |
| kernel: K, |
| entry_point: fn(K, A), |
| arg: A, |
| ) { |
| kernel |
| .get_scheduler() |
| .lock(kernel) |
| .thread_initialize_kernel(kernel, self, entry_point, arg) |
| } |
| |
| pub(super) fn set_process(&mut self, process: ProcessHandle<K>) { |
| self.process = Some(process); |
| } |
| |
| #[cfg(feature = "user_space")] |
| /// # Safety |
| /// It is up to the caller to ensure that *process is valid. |
| /// Initialize the mutable parts of the non privileged thread, must be |
| /// called once per thread prior to starting it |
| pub unsafe fn initialize_non_priv_thread( |
| &mut self, |
| kernel: K, |
| process: ProcessHandle<K>, |
| initial_pc: usize, |
| initial_sp: usize, |
| args: (usize, usize, usize), |
| ) -> Result<()> { |
| unsafe { |
| kernel |
| .get_scheduler() |
| .lock(kernel) |
| .thread_initialize_non_priv(kernel, self, process, initial_pc, initial_sp, args) |
| } |
| } |
| |
| #[allow(dead_code)] |
| pub fn dump(&self) { |
| info!( |
| " - Thread '{}' ({:#010x}) state: {}", |
| self.name as &str, |
| self.id() as usize, |
| to_string(self.state) as &str |
| ); |
| } |
| |
| /// Returns a reference to the thread's parent process. |
| pub fn process(&self) -> ProcessHandle<K> { |
| // SAFETY: The returned process references is bound to an immutable |
| // borrow of the thread the `process` pointer can not change. |
| let Some(process_handle) = self.process.as_ref() else { |
| pw_assert::panic!("Thread does not have a process"); |
| }; |
| process_handle.clone() |
| } |
| |
| /// Return a reference counted `ThreadHandle` for this thread. |
| pub(super) fn get_ref(&self, kernel: K) -> ThreadHandle<K> { |
| self.ref_count.fetch_add(1, Ordering::Acquire); |
| ThreadHandle { |
| thread: NonNull::from_ref(self), |
| kernel, |
| } |
| } |
| |
| /// A simple ID for debugging purposes |
| /// |
| /// Currently this is a pointer to the architecture specific thread state |
| /// allowing it to match debugging output from the architecture implementation. |
| /// |
| /// # Safety |
| /// |
| /// The returned value should not be relied upon as being a valid pointer. |
| /// Even in the current implementation, `id` does not expose the pointer's |
| /// provenance. |
| #[must_use] |
| pub fn id(&self) -> usize { |
| // Note: any changes to this also need to be reflected in |
| // `const_id_from_thread_address`. |
| self.arch_thread_state.get().addr() |
| } |
| |
| /// Computes the ID of a thread from its address. |
| /// |
| /// # Safety |
| /// |
| /// This exists to allow thread annotations to be generated at compile time |
| /// for static threads that are initialized at runtime. This should not be |
| /// called in any other circumstance. |
| #[must_use] |
| pub const unsafe fn const_id_from_thread_address(addr: *const ()) -> *const () { |
| unsafe { addr.byte_add(core::mem::offset_of!(Self, arch_thread_state)) } |
| } |
| |
| // An ID that can not be assigned to any thread in the system. |
| #[must_use] |
| pub const fn null_id() -> usize { |
| // `core::ptr::null::<Self>() as usize` can not be evaluated at const time |
| // and a null pointer is defined to be at address 0 (see |
| // https://doc.rust-lang.org/beta/core/ptr/fn.null.html). |
| 0usize |
| } |
| } |
| |
| pub use arg::ThreadArg; |
| mod arg { |
| pub trait ThreadArg { |
| fn into_usize(self) -> usize; |
| |
| /// # Safety |
| /// |
| /// `u` must have previously been returned by [`x.into_usize()`]. The |
| /// returned `Self` is guaranteed to be equal to `x`. |
| /// |
| /// [`x.into_usize()`]: ThreadArg::into_usize |
| unsafe fn from_usize(u: usize) -> Self; |
| } |
| |
| impl ThreadArg for usize { |
| fn into_usize(self) -> usize { |
| self |
| } |
| |
| unsafe fn from_usize(u: usize) -> Self { |
| u |
| } |
| } |
| |
| impl<T> ThreadArg for *const T { |
| fn into_usize(self) -> usize { |
| self.expose_provenance() |
| } |
| |
| unsafe fn from_usize(u: usize) -> Self { |
| core::ptr::with_exposed_provenance(u) |
| } |
| } |
| |
| impl<T> ThreadArg for *mut T { |
| fn into_usize(self) -> usize { |
| self.expose_provenance() |
| } |
| |
| unsafe fn from_usize(u: usize) -> Self { |
| core::ptr::with_exposed_provenance_mut(u) |
| } |
| } |
| |
| #[allow(clippy::needless_lifetimes)] |
| impl<'a, T> ThreadArg for &'a T { |
| fn into_usize(self) -> usize { |
| let s: *const T = self; |
| s.into_usize() |
| } |
| |
| unsafe fn from_usize(u: usize) -> Self { |
| // SAFETY: The caller promises that `u` was previously returned by |
| // `into_usize`, which is implemented as `<*const T as |
| // ThreadArg>::into_usize`. Thus, `u` was previously returned by |
| // `<*const T as ThreadArg>::into_usize`. |
| let ptr = unsafe { <*const T>::from_usize(u) }; |
| // SAFETY: By the preceding safety comment, `ptr` is equal to `self |
| // as *const T` where `self: &'a T`, including provenance. |
| unsafe { &*ptr } |
| } |
| } |
| |
| #[allow(clippy::needless_lifetimes)] |
| impl<'a, T> ThreadArg for &'a mut T { |
| fn into_usize(self) -> usize { |
| let s: *mut T = self; |
| s.into_usize() |
| } |
| |
| unsafe fn from_usize(u: usize) -> Self { |
| // SAFETY: The caller promises that `u` was previously returned by |
| // `into_usize`, which is implemented as `<*mut T as |
| // ThreadArg>::into_usize`. Thus, `u` was previously returned by |
| // `<*mut T as ThreadArg>::into_usize`. |
| let ptr = unsafe { <*mut T>::from_usize(u) }; |
| // SAFETY: By the preceding safety comment, `ptr` is equal to `self |
| // as *mut T` where `self: &'a mut T`, including provenance. Since |
| // `into_usize` consumes `self` by value, no other references to the |
| // same referent exist, and so mutable aliasing is satisfied. |
| unsafe { &mut *ptr } |
| } |
| } |
| |
| #[macro_export] |
| macro_rules! impl_thread_arg_for_default_zst { |
| ($t:ty) => { |
| const _: () = assert!(size_of::<$t>() == 0); |
| impl $crate::scheduler::ThreadArg for $t { |
| fn into_usize(self) -> usize { |
| 0 |
| } |
| |
| unsafe fn from_usize(_u: usize) -> Self { |
| // SAFETY: We asserted above that `size_of::<$t>() == 0`, so |
| // there is only one value of `Self`. Thus, this |
| // implementation of `from_usize` returns the same value |
| // passed to any call to `into_usize`, as there is only one |
| // possible such value. |
| <$t as Default>::default() |
| } |
| } |
| }; |
| } |
| } |
| |
| /// Add debug annotations for a static thread |
| /// |
| /// This macro is called by `init_non_priv_thread` and should not be called |
| /// directly. |
| #[macro_export] |
| macro_rules! annotate_thread_from_address { |
| ($name:expr, $arch:ty, $thread_address:expr, $parent_id: expr) => {{ |
| #[repr(C, packed(1))] |
| struct ThreadAnnotation { |
| name: &'static str, |
| id: *const (), |
| parent_id: *const (), |
| } |
| unsafe impl Sync for ThreadAnnotation {}; |
| |
| #[unsafe(link_section = ".pw_kernel.annotations.thread")] |
| #[used] |
| static _THREAD_ANNOTATION: ThreadAnnotation = ThreadAnnotation { |
| name: $name, |
| id: unsafe { |
| $crate::Thread::<$arch>::const_id_from_thread_address($thread_address as *const ()) |
| }, |
| parent_id: $parent_id as *const (), |
| }; |
| }}; |
| } |
| |
| /// Add debug annotations for a stack. |
| /// |
| /// This macro is called by `init_process` and should not be called directly. |
| #[macro_export] |
| macro_rules! annotate_stack { |
| ($name:expr, $addr:expr, $size:expr) => {{ |
| #[repr(C, packed(1))] |
| struct StackAnnotation { |
| name: &'static str, |
| addr: *const (), |
| size: usize, |
| } |
| unsafe impl Sync for StackAnnotation {}; |
| |
| #[unsafe(link_section = ".pw_kernel.annotations.stack")] |
| #[used] |
| static _STACK_ANNOTATION: StackAnnotation = StackAnnotation { |
| name: $name, |
| addr: $addr as *const (), |
| size: $size, |
| }; |
| }}; |
| } |
| |
| /// Declare a static stack. |
| /// |
| /// This macro is called by `init_process` and should not be called directly. |
| #[macro_export] |
| macro_rules! static_stack { |
| ($thread_name:expr, $stack_size:expr) => {{ |
| use core::cell::UnsafeCell; |
| use core::mem::MaybeUninit; |
| |
| use kernel::{StackStorage, StackStorageExt}; |
| |
| static mut __STATIC: UnsafeCell<MaybeUninit<StackStorage<$stack_size>>> = |
| UnsafeCell::new(core::mem::MaybeUninit::uninit()); |
| $crate::annotate_stack!($thread_name, unsafe { __STATIC.get() }, $stack_size); |
| |
| unsafe { (*__STATIC.get()).write(StackStorageExt::ZEROED) } |
| }}; |
| } |
| |
| /// Constructs a new [`Thread`] in global static storage. |
| /// |
| /// # Safety |
| /// |
| /// Each invocation of `init_thread!` must be executed at most once at run time. |
| // TODO: davidroth - Add const assertions to ensure stack sizes aren't too |
| // small, once the sizing analysis has been done to understand what a reasonable |
| // minimum is. |
| #[macro_export] |
| macro_rules! init_thread { |
| ($name:literal, $priority:expr, $entry:expr, $stack_size:expr $(,)?) => {{ |
| use $crate::__private::foreign_box::ForeignBox; |
| use $crate::scheduler::{Stack, StackStorage, StackStorageExt, Thread}; |
| use $crate::static_mut_ref; |
| |
| /// SAFETY: This must be executed at most once at run time. |
| unsafe fn __init_thread() -> ForeignBox<Thread<arch::Arch>> { |
| info!("Initializing thread '{}'", $name as &'static str); |
| let stack = $crate::static_stack!($name, $stack_size); |
| // SAFETY: The caller promises that this function will be |
| // executed at most once. |
| let stack = Stack::from_slice(&stack.stack); |
| |
| let thread = unsafe { static_mut_ref!(Thread<arch::Arch> = Thread::new($name, $priority, stack)) }; |
| let mut thread = ForeignBox::from(thread); |
| |
| thread.initialize_kernel_thread( |
| $crate::arch::Arch, |
| $entry, |
| 0 |
| ); |
| |
| thread |
| } |
| |
| __init_thread() |
| }}; |
| } |
| |
| /// Constructs a new [`Thread`] in global static storage and registers it. |
| /// |
| /// # Safety |
| /// |
| /// Each invocation of `declare_non_priv_thread!` must be executed at most once at |
| /// run time. |
| #[cfg(feature = "user_space")] |
| #[macro_export] |
| macro_rules! declare_non_priv_thread { |
| ($name:literal, $priority:expr, $process_id:expr, $kernel_stack_size:expr $(,)?) => {{ |
| use core::mem::MaybeUninit; |
| |
| use $crate::__private::foreign_box::ForeignBox; |
| use $crate::scheduler::{Stack, StackStorage, StackStorageExt, Thread}; |
| |
| /// SAFETY: This must be executed at most once at run time. |
| unsafe fn __init_non_priv_thread() -> (ForeignBox<Thread<arch::Arch>>, Stack) { |
| use pw_log::info; |
| info!( |
| "Allocating non-privileged thread '{}'", |
| $name as &'static str |
| ); |
| // SAFETY: The caller promises that this function will be executed |
| // at most once. |
| use $crate::__private::foreign_box::StaticStorage; |
| static mut __STATIC: StaticStorage<Thread<arch::Arch>> = StaticStorage::new(); |
| $crate::annotate_thread_from_address!( |
| $name, |
| arch::Arch, |
| unsafe { __STATIC.address() }, |
| $process_id |
| ); |
| let stack = $crate::static_stack!($name, $kernel_stack_size); |
| // SAFETY: The caller promises that this function will be |
| // executed at most once. |
| let stack = Stack::from_slice(&stack.stack); |
| |
| let thread = __STATIC.init(Thread::new($name, $priority, stack)); |
| let mut thread = ForeignBox::from(thread); |
| |
| (thread, stack) |
| } |
| |
| __init_non_priv_thread() |
| }}; |
| } |
| |
| /// Initializes a thread in the given storage. |
| pub fn init_thread_in<K: Kernel, T: ThreadArg, const STACK_SIZE: usize>( |
| kernel: K, |
| thread: &'static mut Thread<K>, |
| stack: &'static mut StackStorage<STACK_SIZE>, |
| name: &'static str, |
| priority: Priority, |
| entry: fn(K, T), |
| arg: T, |
| ) -> ForeignBox<Thread<K>> { |
| let stack_obj = Stack::from_slice(&stack.stack); |
| *thread = Thread::new(name, priority, stack_obj); |
| let mut thread = ForeignBox::from(thread); |
| thread.initialize_kernel_thread(kernel, entry, arg); |
| thread |
| } |