Skip to content

Commit 1780b80

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 1780b80

File tree

2 files changed

+196
-0
lines changed

2 files changed

+196
-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

+195
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,195 @@
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. These should not be declared directly by
43+
/// the user, as they generally need linker decorations to be properly registered in Zephyr as
44+
/// kernel objects. The object has the underlying Zephyr type T, and the wrapper type W.
45+
/// TODO: Handling const-defined alignment for these.
46+
pub struct StaticKernelObject<T> {
47+
/// The underlying zephyr kernel object.
48+
value: UnsafeCell<T>,
49+
/// Initialization status of this object. Most objects will start uninitialized and be
50+
/// initialized manually.
51+
#[cfg(CONFIG_RUST_CHECK_KOBJ_INIT)]
52+
init: AtomicUsize,
53+
}
54+
55+
/// A kernel object that has a way to get a raw pointer.
56+
///
57+
/// Generally, kernel objects in Rust are just containers for raw pointers to an underlying kernel
58+
/// object. When this is the case, this trait can be used to get the underlying pointer.
59+
pub trait KobjGet<T> {
60+
fn get_ptr(&self) -> *mut T;
61+
}
62+
63+
impl<T> KobjGet<T> for StaticKernelObject<T> {
64+
fn get_ptr(&self) -> *mut T {
65+
self.value.get()
66+
}
67+
}
68+
69+
/// A state indicating an uninitialized kernel object.
70+
///
71+
/// This must be zero, as kernel objects will
72+
/// be represetned as zero-initialized memory.
73+
pub const KOBJ_UNINITIALIZED: usize = 0;
74+
75+
/// A state indicating a kernel object that is being initialized.
76+
pub const KOBJ_INITING: usize = 1;
77+
78+
/// A state indicating a kernel object that has completed initialization.
79+
pub const KOBJ_INITIALIZED: usize = 2;
80+
81+
impl<T> StaticKernelObject<T> {
82+
/// Construct an empty of these objects, with the zephyr data zero-filled. This is safe in the
83+
/// sense that Zephyr we track the initialization, and they start in the uninitialized state.
84+
pub const fn new() -> StaticKernelObject<T> {
85+
StaticKernelObject {
86+
value: unsafe { mem::zeroed() },
87+
#[cfg(CONFIG_RUST_CHECK_KOBJ_INIT)]
88+
init: AtomicUsize::new(KOBJ_UNINITIALIZED),
89+
}
90+
}
91+
92+
/// An initialization helper. Runs the code in `f` if the object is uninitialized. Panics if
93+
/// the initialization state is incorrect.
94+
#[cfg(CONFIG_RUST_CHECK_KOBJ_INIT)]
95+
pub fn init_help<R, F: FnOnce(*mut T) -> R>(&self, f: F) -> R {
96+
if let Err(_) = self.init.compare_exchange(
97+
KOBJ_UNINITIALIZED,
98+
KOBJ_INITING,
99+
Ordering::AcqRel,
100+
Ordering::Acquire)
101+
{
102+
panic!("Duplicate kobject initialization");
103+
}
104+
let result = f(self.get_ptr());
105+
self.init.store(KOBJ_INITIALIZED, Ordering::Release);
106+
result
107+
}
108+
109+
/// An initialization helper. Runs the code in `f` if the object is uninitialized. Panics if
110+
/// the initialization state is incorrect.
111+
#[cfg(not(CONFIG_RUST_CHECK_KOBJ_INIT))]
112+
pub fn init_help<R, F: FnOnce(*mut T) -> R>(&self, f: F) -> R {
113+
f(self.get_ptr())
114+
}
115+
}
116+
117+
/// Kernel object wrappers implement this trait so construct themselves out of the underlying
118+
/// pointer.
119+
pub trait KobjInit<T, W> where Self: KobjGet<T> + Sized {
120+
/// Get an instance of the kernel object.
121+
///
122+
/// This instance is wrapped in the kernel static object wreapper. Generally, the trait
123+
/// implementation is sufficient.
124+
fn get(&self) -> W {
125+
let ptr = self.get_ptr();
126+
Self::wrap(ptr)
127+
}
128+
129+
/// Wrap the underlying pointer itself.
130+
fn wrap(ptr: *mut T) -> W;
131+
}
132+
133+
/// Declare a static kernel object. This helps declaring static values of Zephyr objects.
134+
///
135+
/// This can typically be used as:
136+
/// ```
137+
/// kobj_define! {
138+
/// static A_MUTEX: StaticMutex;
139+
/// static MUTEX_ARRAY: [StaticMutex; 4];
140+
/// }
141+
/// ```
142+
#[macro_export]
143+
macro_rules! kobj_define {
144+
($v:vis static $name:ident: $type:tt; $($rest:tt)*) => {
145+
$crate::_kobj_rule!($v, $name, $type);
146+
$crate::kobj_define!($($rest)*);
147+
};
148+
($v:vis static $name:ident: $type:tt<$size:ident>; $($rest:tt)*) => {
149+
$crate::_kobj_rule!($v, $name, $type<$size>);
150+
$crate::kobj_define!($($rest)*);
151+
};
152+
($v:vis static $name:ident: $type:tt<$size:literal>; $($rest:tt)*) => {
153+
$crate::_kobj_rule!($v, $name, $type<$size>);
154+
$crate::kobj_define!($($rest)*);
155+
};
156+
($v:vis static $name:ident: $type:tt<{$size:expr}>; $($rest:tt)*) => {
157+
$crate::_kobj_rule!($v, $name, $type<{$size}>);
158+
$crate::kobj_define!($($rest)*);
159+
};
160+
() => {};
161+
}
162+
163+
#[doc(hidden)]
164+
#[macro_export]
165+
macro_rules! _kobj_rule {
166+
($v:vis, $name:ident, StaticMutex) => {
167+
#[link_section = concat!("._k_mutex.static.", stringify!($name), ".", file!(), line!())]
168+
$v static $name: $crate::sys::sync::StaticMutex =
169+
$crate::sys::sync::StaticMutex::new();
170+
};
171+
($v:vis, $name:ident, [StaticMutex; $size:expr]) => {
172+
#[link_section = concat!("._k_mutex.static.", stringify!($name), ".", file!(), line!())]
173+
$v static $name: [$crate::sys::sync::StaticMutex; $size] =
174+
// This isn't Copy, intentionally, so initialize the whole thing with zerored memory.
175+
// Relying on the atomic to be 0 for the uninitialized state.
176+
// [$crate::sys::sync::StaticMutex::new(); $size];
177+
unsafe { ::core::mem::zeroed() };
178+
};
179+
}
180+
181+
#[doc(hidden)]
182+
#[macro_export]
183+
macro_rules! _kobj_stack {
184+
($v:vis, $name:ident, $size:expr) => {
185+
#[link_section = concat!(".noinit.", stringify!($name), ".", file!(), line!())]
186+
$v static $name: $crate::sys::thread::ThreadStack<{$crate::sys::thread::stack_len($size)}>
187+
= unsafe { ::core::mem::zeroed() };
188+
};
189+
190+
($v:vis, $name:ident, $size:expr, $asize:expr) => {
191+
#[link_section = concat!(".noinit.", stringify!($name), ".", file!(), line!())]
192+
$v static $name: [$crate::sys::thread::ThreadStack<{$crate::sys::thread::stack_len($size)}>; $asize]
193+
= unsafe { ::core::mem::zeroed() };
194+
};
195+
}

0 commit comments

Comments
 (0)