|
| 1 | +//! Zephyr low level threads |
| 2 | +//! |
| 3 | +//! This is a fairly low level (but still safe) interface to Zephyr threads. This is intended to |
| 4 | +//! work the same way as threads are typically done on Zephyr systems, where the threads and their |
| 5 | +//! stacks are statically allocated, a code is called to initialize them. |
| 6 | +//! |
| 7 | +//! In addition, there are some convenience operations available that require allocation to be |
| 8 | +//! available. |
| 9 | +
|
| 10 | +use zephyr_sys::{ |
| 11 | + k_thread, k_thread_create, k_thread_start, z_thread_stack_element, ZR_STACK_ALIGN, ZR_STACK_RESERVED |
| 12 | +}; |
| 13 | + |
| 14 | +use core::{cell::UnsafeCell, ffi::c_void, ptr::null_mut}; |
| 15 | + |
| 16 | +use crate::{align::AlignAs, object::{KobjInit, StaticKernelObject}}; |
| 17 | + |
| 18 | +#[cfg(CONFIG_RUST_ALLOC)] |
| 19 | +extern crate alloc; |
| 20 | +#[cfg(CONFIG_RUST_ALLOC)] |
| 21 | +use alloc::boxed::Box; |
| 22 | +#[cfg(CONFIG_RUST_ALLOC)] |
| 23 | +use core::mem::ManuallyDrop; |
| 24 | + |
| 25 | +use super::K_FOREVER; |
| 26 | + |
| 27 | +/// Adjust the stack size for alignment. Note that, unlike the C code, we don't include the |
| 28 | +/// reservation in this, as it has its own fields in the struct. |
| 29 | +pub const fn stack_len(size: usize) -> usize { |
| 30 | + size.next_multiple_of(ZR_STACK_ALIGN) |
| 31 | +} |
| 32 | + |
| 33 | +/// A Zephyr stack declaration. It isn't meant to be used directly, as it needs additional |
| 34 | +/// decoration about linker sections and such. Unlike the C declaration, the reservation is a |
| 35 | +/// separate field. As long as the SIZE is properly aligned, this should work without padding |
| 36 | +/// between the fields. |
| 37 | +pub struct ThreadStack<const SIZE: usize> { |
| 38 | + pub align: AlignAs<ZR_STACK_ALIGN>, |
| 39 | + pub data: UnsafeCell<[z_thread_stack_element; SIZE]>, |
| 40 | + pub extra: [z_thread_stack_element; ZR_STACK_RESERVED], |
| 41 | +} |
| 42 | + |
| 43 | +unsafe impl<const SIZE: usize> Sync for ThreadStack<SIZE> {} |
| 44 | + |
| 45 | +impl<const SIZE: usize> ThreadStack<SIZE> { |
| 46 | + /// Get the size of this stack. This is the size, minus any reservation. This is called `size` |
| 47 | + /// to avoid any confusion with `len` which might return the actual size of the stack. |
| 48 | + pub fn size(&self) -> usize { |
| 49 | + SIZE |
| 50 | + } |
| 51 | + |
| 52 | + /// Return the stack base needed as the argument to various zephyr calls. |
| 53 | + pub fn base(&self) -> *mut z_thread_stack_element { |
| 54 | + self.data.get() as *mut z_thread_stack_element |
| 55 | + } |
| 56 | + |
| 57 | + /// Return the token information for this stack, which is a base and size. |
| 58 | + pub fn token(&self) -> StackToken { |
| 59 | + StackToken { base: self.base(), size: self.size() } |
| 60 | + } |
| 61 | +} |
| 62 | + |
| 63 | +/// Declare a variable, of a given name, representing the stack for a thread. |
| 64 | +#[macro_export] |
| 65 | +macro_rules! kernel_stack_define { |
| 66 | + ($name:ident, $size:expr) => { |
| 67 | + #[link_section = concat!(".noinit.", stringify!($name), ".", file!(), line!())] |
| 68 | + static $name: $crate::sys::thread::ThreadStack<{$crate::sys::thread::stack_len($size)}> |
| 69 | + = unsafe { ::core::mem::zeroed() }; |
| 70 | + }; |
| 71 | +} |
| 72 | + |
| 73 | +pub struct Thread { |
| 74 | + pub raw: *mut k_thread, |
| 75 | +} |
| 76 | + |
| 77 | +unsafe impl Sync for StaticKernelObject<k_thread> { } |
| 78 | + |
| 79 | +impl KobjInit<k_thread, Thread> for StaticKernelObject<k_thread> { |
| 80 | + fn wrap(ptr: *mut k_thread) -> Thread { |
| 81 | + Thread { raw: ptr } |
| 82 | + } |
| 83 | +} |
| 84 | + |
| 85 | +// Public interface to threads. |
| 86 | +impl Thread { |
| 87 | + /// Start execution of the given thread. |
| 88 | + pub fn start(&self) { |
| 89 | + unsafe { k_thread_start(self.raw) } |
| 90 | + } |
| 91 | +} |
| 92 | + |
| 93 | +/// Declare a global static representing a thread variable. |
| 94 | +#[macro_export] |
| 95 | +macro_rules! kernel_thread_define { |
| 96 | + ($name:ident) => { |
| 97 | + // Since the static object has an atomic that we assume is initialized, let the compiler put |
| 98 | + // this in the data section it finds appropriate (probably .bss if it is initialized to zero). |
| 99 | + // This only matters when the objects are being checked. |
| 100 | + // TODO: This doesn't seem to work with the config. |
| 101 | + // #[cfg_attr(not(CONFIG_RUST_CHECK_KOBJ_INIT), |
| 102 | + // link_section = concat!(".noinit.", stringify!($name), ".", file!(), line!()))] |
| 103 | + static $name: $crate::object::StaticKernelObject<$crate::raw::k_thread> = |
| 104 | + $crate::object::StaticKernelObject::new(); |
| 105 | + // static $name: $crate::sys::thread::Thread = unsafe { ::core::mem::zeroed() }; |
| 106 | + }; |
| 107 | +} |
| 108 | + |
| 109 | +/// For now, this "token" represents the somewhat internal information about thread. |
| 110 | +/// What we really want is to make sure that stacks and threads go together. |
| 111 | +pub struct StackToken { |
| 112 | + base: *mut z_thread_stack_element, |
| 113 | + size: usize, |
| 114 | +} |
| 115 | + |
| 116 | +// This isn't really safe at all, as these can be initialized. It is unclear how, if even if it is |
| 117 | +// possible to implement safe static threads and other data structures in Zephyr. |
| 118 | + |
| 119 | +pub type StaticThread = StaticKernelObject<k_thread>; |
| 120 | + |
| 121 | +// The thread itself assumes we've already initialized, so this method is on the wrapper. |
| 122 | +impl StaticThread { |
| 123 | + /// Spawn this thread to the given external function. This is a simplified version that doesn't |
| 124 | + /// take any arguments. The child runs immediately. |
| 125 | + pub fn simple_spawn(&self, stack: StackToken, child: fn() -> ()) -> Thread { |
| 126 | + self.init_help(|raw| { |
| 127 | + unsafe { |
| 128 | + k_thread_create( |
| 129 | + raw, |
| 130 | + stack.base, |
| 131 | + stack.size, |
| 132 | + Some(simple_child), |
| 133 | + child as *mut c_void, |
| 134 | + null_mut(), |
| 135 | + null_mut(), |
| 136 | + 5, |
| 137 | + 0, |
| 138 | + K_FOREVER, |
| 139 | + ); |
| 140 | + } |
| 141 | + }); |
| 142 | + self.get() |
| 143 | + } |
| 144 | + |
| 145 | + #[cfg(CONFIG_RUST_ALLOC)] |
| 146 | + /// Spawn a thread, running a closure. The closure will be boxed to give to the new thread. |
| 147 | + /// The new thread runs immediately. |
| 148 | + pub fn spawn<F: FnOnce() + Send + 'static>(&self, stack: StackToken, child: F) -> Thread { |
| 149 | + let child: closure::Closure = Box::new(child); |
| 150 | + let child = Box::into_raw(Box::new(closure::ThreadData { |
| 151 | + closure: ManuallyDrop::new(child), |
| 152 | + })); |
| 153 | + self.init_help(move |raw| { |
| 154 | + unsafe { |
| 155 | + k_thread_create( |
| 156 | + raw, |
| 157 | + stack.base, |
| 158 | + stack.size, |
| 159 | + Some(closure::child), |
| 160 | + child as *mut c_void, |
| 161 | + null_mut(), |
| 162 | + null_mut(), |
| 163 | + 5, |
| 164 | + 0, |
| 165 | + K_FOREVER, |
| 166 | + ); |
| 167 | + } |
| 168 | + }); |
| 169 | + self.get() |
| 170 | + } |
| 171 | +} |
| 172 | + |
| 173 | +unsafe extern "C" fn simple_child( |
| 174 | + arg: *mut c_void, |
| 175 | + _p2: *mut c_void, |
| 176 | + _p3: *mut c_void, |
| 177 | +) { |
| 178 | + let child: fn() -> () = core::mem::transmute(arg); |
| 179 | + (child)(); |
| 180 | +} |
| 181 | + |
| 182 | +#[cfg(CONFIG_RUST_ALLOC)] |
| 183 | +/// Handle the closure case. This invokes a double box to rid us of the fat pointer. I'm not sure |
| 184 | +/// this is actually necessary. |
| 185 | +mod closure { |
| 186 | + use core::{ffi::c_void, mem::ManuallyDrop}; |
| 187 | + use super::Box; |
| 188 | + |
| 189 | + pub type Closure = Box<dyn FnOnce()>; |
| 190 | + |
| 191 | + pub struct ThreadData { |
| 192 | + pub closure: ManuallyDrop<Closure>, |
| 193 | + } |
| 194 | + |
| 195 | + pub unsafe extern "C" fn child(child: *mut c_void, _p2: *mut c_void, _p3: *mut c_void) { |
| 196 | + let mut thread_data: Box<ThreadData> = unsafe { Box::from_raw(child as *mut ThreadData) }; |
| 197 | + let closure = unsafe { ManuallyDrop::take(&mut (*thread_data).closure) }; |
| 198 | + closure(); |
| 199 | + } |
| 200 | +} |
0 commit comments