blob: 6baffa369a2621d1bd77792b8a81357d843ea4ca [file]
// 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
}