Skip to content

Commit 2400c74

Browse files
committed
zephyr: Introduce static kobject support
Create wrappers for static kernel objects. This is done through a few wrapper types. Generally, a StaticKernelObject which contains a 'value' which is an unsafe cell containing the actual zephyr-typed kernel object, and then some traits to use these, specifically `KobjGet` to fetch the underlying pointer, and `KobjInit` which supports initialization and getting a higher-level wrapper around the underlying object pointer. Signed-off-by: David Brown <[email protected]>
1 parent 37e80f1 commit 2400c74

File tree

2 files changed

+199
-0
lines changed

2 files changed

+199
-0
lines changed

zephyr/src/lib.rs

+1
Original file line numberDiff line numberDiff line change
@@ -84,6 +84,7 @@
8484
#![no_std]
8585
#![allow(unexpected_cfgs)]
8686

87+
pub mod object;
8788
pub mod sync;
8889
pub mod sys;
8990
pub mod time;

zephyr/src/object.rs

+198
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,198 @@
1+
//! # Zephyr Kernel Objects
2+
//!
3+
//! Zephyr has a concept of a 'kernel object' that is handled a bit magically. In kernel mode
4+
//! threads, these are just pointers to the data structures that Zephyr uses to manage that item.
5+
//! In userspace, they are still pointers, but those data structures aren't accessible to the
6+
//! thread. When making syscalls, the kernel validates that the objects are both valid kernel
7+
//! objects and that the are supposed to be accessible to this thread.
8+
//!
9+
//! In many Zephyr apps, the kernel objects in the app are defined as static, using special macros.
10+
//! These macros make sure that the objects get registered so that they are accessible to userspace
11+
//! (at least after that access is granted).
12+
//!
13+
//! There are also kernel objects that are synthesized as part of the build. Most notably, there
14+
//! are ones generated by the device tree.
15+
//!
16+
//! There are some funny rules about references and mutable references to memory that is
17+
//! inaccessible. Basically, it is never valid to have a reference to something that isn't
18+
//! accessible. However, we can have `*mut ktype` or `*const ktype`. In Rust, having multiple
19+
//! `*mut` pointers live is undefined behavior. Zephyr makes extensive use of shared mutable
20+
//! pointers (or sometimes immutable). We will not dereference these in Rust code, but merely pass
21+
//! them to APIs in Zephyr that require them.
22+
//!
23+
//! Most kernel objects use mutable pointers, and it makes sense to require the wrapper structure
24+
//! to be mutable to these accesses. There a few cases, mostly generated ones that live in
25+
//! read-only memory, notably device instances, that need const pointers. These will be
26+
//! represented by a separate wrapper.
27+
//!
28+
//! # Initialization tracking
29+
//!
30+
//! The Kconfig `CONFIG_RUST_CHECK_KOBJ_INIT` enabled extra checking in Rust-based kernel objects.
31+
//! This will result in a panic if the objects are used before the underlying object has been
32+
//! initialized. The initialization must happen through the `StaticKernelObject::init_help`
33+
//! method.
34+
//!
35+
//! TODO: Document how the wrappers work once we figure out how to implement them.
36+
37+
use core::{cell::UnsafeCell, mem};
38+
39+
#[cfg(CONFIG_RUST_CHECK_KOBJ_INIT)]
40+
use crate::sync::atomic::{AtomicUsize, Ordering};
41+
42+
/// A kernel object represented statically in Rust code.
43+
///
44+
/// These should not be declared directly by the user, as they generally need linker decorations to
45+
/// be properly registered in Zephyr as kernel objects. The object has the underlying Zephyr type
46+
/// T, and the wrapper type W.
47+
///
48+
/// TODO: Handling const-defined alignment for these.
49+
pub struct StaticKernelObject<T> {
50+
/// The underlying zephyr kernel object.
51+
value: UnsafeCell<T>,
52+
/// Initialization status of this object. Most objects will start uninitialized and be
53+
/// initialized manually.
54+
#[cfg(CONFIG_RUST_CHECK_KOBJ_INIT)]
55+
init: AtomicUsize,
56+
}
57+
58+
/// A kernel object that has a way to get a raw pointer.
59+
///
60+
/// Generally, kernel objects in Rust are just containers for raw pointers to an underlying kernel
61+
/// object. When this is the case, this trait can be used to get the underlying pointer.
62+
pub trait KobjGet<T> {
63+
fn get_ptr(&self) -> *mut T;
64+
}
65+
66+
impl<T> KobjGet<T> for StaticKernelObject<T> {
67+
fn get_ptr(&self) -> *mut T {
68+
self.value.get()
69+
}
70+
}
71+
72+
/// A state indicating an uninitialized kernel object.
73+
///
74+
/// This must be zero, as kernel objects will
75+
/// be represetned as zero-initialized memory.
76+
pub const KOBJ_UNINITIALIZED: usize = 0;
77+
78+
/// A state indicating a kernel object that is being initialized.
79+
pub const KOBJ_INITING: usize = 1;
80+
81+
/// A state indicating a kernel object that has completed initialization.
82+
pub const KOBJ_INITIALIZED: usize = 2;
83+
84+
impl<T> StaticKernelObject<T> {
85+
/// Construct an empty of these objects, with the zephyr data zero-filled. This is safe in the
86+
/// sense that Zephyr we track the initialization, and they start in the uninitialized state.
87+
pub const fn new() -> StaticKernelObject<T> {
88+
StaticKernelObject {
89+
value: unsafe { mem::zeroed() },
90+
#[cfg(CONFIG_RUST_CHECK_KOBJ_INIT)]
91+
init: AtomicUsize::new(KOBJ_UNINITIALIZED),
92+
}
93+
}
94+
95+
/// An initialization helper. Runs the code in `f` if the object is uninitialized. Panics if
96+
/// the initialization state is incorrect.
97+
#[cfg(CONFIG_RUST_CHECK_KOBJ_INIT)]
98+
pub fn init_help<R, F: FnOnce(*mut T) -> R>(&self, f: F) -> R {
99+
if let Err(_) = self.init.compare_exchange(
100+
KOBJ_UNINITIALIZED,
101+
KOBJ_INITING,
102+
Ordering::AcqRel,
103+
Ordering::Acquire)
104+
{
105+
panic!("Duplicate kobject initialization");
106+
}
107+
let result = f(self.get_ptr());
108+
self.init.store(KOBJ_INITIALIZED, Ordering::Release);
109+
result
110+
}
111+
112+
/// An initialization helper. Runs the code in `f` if the object is uninitialized. Panics if
113+
/// the initialization state is incorrect.
114+
#[cfg(not(CONFIG_RUST_CHECK_KOBJ_INIT))]
115+
pub fn init_help<R, F: FnOnce(*mut T) -> R>(&self, f: F) -> R {
116+
f(self.get_ptr())
117+
}
118+
}
119+
120+
/// Kernel object wrappers implement this trait so construct themselves out of the underlying
121+
/// pointer.
122+
pub trait KobjInit<T, W> where Self: KobjGet<T> + Sized {
123+
/// Get an instance of the kernel object.
124+
///
125+
/// This instance is wrapped in the kernel static object wreapper. Generally, the trait
126+
/// implementation is sufficient.
127+
fn get(&self) -> W {
128+
let ptr = self.get_ptr();
129+
Self::wrap(ptr)
130+
}
131+
132+
/// Wrap the underlying pointer itself.
133+
fn wrap(ptr: *mut T) -> W;
134+
}
135+
136+
/// Declare a static kernel object. This helps declaring static values of Zephyr objects.
137+
///
138+
/// This can typically be used as:
139+
/// ```
140+
/// kobj_define! {
141+
/// static A_MUTEX: StaticMutex;
142+
/// static MUTEX_ARRAY: [StaticMutex; 4];
143+
/// }
144+
/// ```
145+
#[macro_export]
146+
macro_rules! kobj_define {
147+
($v:vis static $name:ident: $type:tt; $($rest:tt)*) => {
148+
$crate::_kobj_rule!($v, $name, $type);
149+
$crate::kobj_define!($($rest)*);
150+
};
151+
($v:vis static $name:ident: $type:tt<$size:ident>; $($rest:tt)*) => {
152+
$crate::_kobj_rule!($v, $name, $type<$size>);
153+
$crate::kobj_define!($($rest)*);
154+
};
155+
($v:vis static $name:ident: $type:tt<$size:literal>; $($rest:tt)*) => {
156+
$crate::_kobj_rule!($v, $name, $type<$size>);
157+
$crate::kobj_define!($($rest)*);
158+
};
159+
($v:vis static $name:ident: $type:tt<{$size:expr}>; $($rest:tt)*) => {
160+
$crate::_kobj_rule!($v, $name, $type<{$size}>);
161+
$crate::kobj_define!($($rest)*);
162+
};
163+
() => {};
164+
}
165+
166+
#[doc(hidden)]
167+
#[macro_export]
168+
macro_rules! _kobj_rule {
169+
($v:vis, $name:ident, StaticMutex) => {
170+
#[link_section = concat!("._k_mutex.static.", stringify!($name), ".", file!(), line!())]
171+
$v static $name: $crate::sys::sync::StaticMutex =
172+
$crate::sys::sync::StaticMutex::new();
173+
};
174+
($v:vis, $name:ident, [StaticMutex; $size:expr]) => {
175+
#[link_section = concat!("._k_mutex.static.", stringify!($name), ".", file!(), line!())]
176+
$v static $name: [$crate::sys::sync::StaticMutex; $size] =
177+
// This isn't Copy, intentionally, so initialize the whole thing with zerored memory.
178+
// Relying on the atomic to be 0 for the uninitialized state.
179+
// [$crate::sys::sync::StaticMutex::new(); $size];
180+
unsafe { ::core::mem::zeroed() };
181+
};
182+
}
183+
184+
#[doc(hidden)]
185+
#[macro_export]
186+
macro_rules! _kobj_stack {
187+
($v:vis, $name:ident, $size:expr) => {
188+
#[link_section = concat!(".noinit.", stringify!($name), ".", file!(), line!())]
189+
$v static $name: $crate::sys::thread::ThreadStack<{$crate::sys::thread::stack_len($size)}>
190+
= unsafe { ::core::mem::zeroed() };
191+
};
192+
193+
($v:vis, $name:ident, $size:expr, $asize:expr) => {
194+
#[link_section = concat!(".noinit.", stringify!($name), ".", file!(), line!())]
195+
$v static $name: [$crate::sys::thread::ThreadStack<{$crate::sys::thread::stack_len($size)}>; $asize]
196+
= unsafe { ::core::mem::zeroed() };
197+
};
198+
}

0 commit comments

Comments
 (0)