13
13
//! There are also kernel objects that are synthesized as part of the build. Most notably, there
14
14
//! are ones generated by the device tree.
15
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.
16
+ //! ## Safety
22
17
//!
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 .
18
+ //! Zephyr has traditionally not focused on safety. Early versions of project goals, in fact,
19
+ //! emphasized performance and small code size as priorities over runtime checking of safety. Over
20
+ //! the years, this focus has changed a bit, and Zephyr does contain some additional checking, some
21
+ //! of which is optional .
27
22
//!
28
- //! # Initialization tracking
23
+ //! Zephyr is still constrained at compile time to checks that can be performed with the limits
24
+ //! of the C language. With Rust, we have a much greater ability to enforce many aspects of safety
25
+ //! at compile time. However, there is some complexity to doing this at the interface between the C
26
+ //! world and Rust.
29
27
//!
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.
28
+ //! There are two types of kernel objects we deal with. There are kernel objects that are allocated
29
+ //! by C code (often auto-generated) that should be accessible to Rust. These are mostly `struct
30
+ //! device` values, and will be handled in a devices module. The other type are objects that
31
+ //! application code wishes to declare statically, and use from Rust code. That is the
32
+ //! responsibility of this module. (There will also be support for more dynamic management of
33
+ //! kernel objects, but this will be handled later).
34
34
//!
35
- //! TODO: Document how the wrappers work once we figure out how to implement them.
35
+ //! Static kernel objects in Zephyr are declared as C top-level variables (where the keyword static
36
+ //! means something different). It is the responsibility of the calling code to initialize these
37
+ //! items, make sure they are only initialized once, and to ensure that sharing of the object is
38
+ //! handled properly. All of these are concerns we can handle in Rust.
39
+ //!
40
+ //! To handle initialization, we pair each kernel object with a single atomic value, whose zero
41
+ //! value indicates [`KOBJ_UNINITIALIZED`]. There are a few instances of values that can be placed
42
+ //! into uninitialized memory in a C declaration that will need to be zero initialized as a Rust
43
+ //! static. The case of thread stacks is handled as a special case, where the initialization
44
+ //! tracking is kept separate so that the stack can still be placed in initialized memory.
45
+ //!
46
+ //! This state goes through two more values as the item is initialized, one indicating the
47
+ //! initialization is happening, and another indicating it has finished.
48
+ //!
49
+ //! For each kernel object, there will be two types. One, having a name of the form StaticThing,
50
+ //! and the other having the form Thing. The StaticThing will be used in a static declaration.
51
+ //! There is a [`kobj_define!`] macro that matches declarations of these values and adds the
52
+ //! necessary linker declarations to place these in the correct linker sections. This is the
53
+ //! equivalent of the set of macros in C, such as `K_SEM_DEFINE`.
54
+ //!
55
+ //! This StaticThing will have a single method [`init_once`] which accepts a single argument of a
56
+ //! type defined by the object. For most objects, it will just be an empty tuple `()`, but it can
57
+ //! be whatever initializer is needed for that type by Zephyr. Semaphores, for example, take the
58
+ //! initial value and the limit. Threads take as an initializer the stack to be used.
59
+ //!
60
+ //! This `init_once` will initialize the Zephyr object and return the `Thing` item that will have
61
+ //! the methods on it to use the object. Attributes such as `Sync`, and `Clone` will be defined
62
+ //! appropriately so as to match the semantics of the underlying Zephyr kernel object. Generally
63
+ //! this `Thing` type will simply be a container for a direct pointer, and thus using and storing
64
+ //! these will have the same characteristics as it would from C.
65
+ //!
66
+ //! Rust has numerous strict rules about mutable references, namely that it is not safe to have more
67
+ //! than one mutable reference. The language does allow multiple `*mut ktype` references, and their
68
+ //! safety depends on the semantics of what is pointed to. In the case of Zephyr, some of these are
69
+ //! intentionally thread safe (for example, things like `k_sem` which have the purpose of
70
+ //! synchronizing between threads). Others are not, and that is mirrored in Rust by whether or not
71
+ //! `Clone` and/or `Sync` are implemented. Please see the documentation of individual entities for
72
+ //! details for that object.
73
+ //!
74
+ //! In general, methods on `Thing` will require `&mut self` if there is any state to manage. Those
75
+ //! that are built around synchronization primitives, however, will generally use `&self`. In
76
+ //! general, objects that implement `Clone` will use `&self` because there would be no benefit to
77
+ //! mutable self when the object could be cloned.
78
+ //!
79
+ //! [`kobj_define!`]: crate::kobj_define
80
+ //! [`init_once`]: StaticKernelObject::init_once
36
81
37
82
use core:: { cell:: UnsafeCell , mem} ;
38
83
39
84
use crate :: sync:: atomic:: { AtomicUsize , Ordering } ;
40
85
86
+ // The kernel object itself must be wrapped in `UnsafeCell` in Rust. This does several thing, but
87
+ // the primary feature that we want to declare to the Rust compiler is that this item has "interior
88
+ // mutability". One impact will be that the default linker section will be writable, even though
89
+ // the object will not be declared as mutable. It also affects the compiler as it will avoid things
90
+ // like aliasing and such on the data, as it will know that it is potentially mutable. In our case,
91
+ // the mutations happen from C code, so this is less important than the data being placed in the
92
+ // proper section. Many will have the link section overridden by the `kobj_define` macro.
93
+
41
94
/// A kernel object represented statically in Rust code.
42
95
///
43
96
/// These should not be declared directly by the user, as they generally need linker decorations to
44
97
/// be properly registered in Zephyr as kernel objects. The object has the underlying Zephyr type
45
98
/// T, and the wrapper type W.
46
99
///
100
+ /// Kernel objects will have their `StaticThing` implemented as `StaticKernelObject<kobj>` where
101
+ /// `kobj` is the type of the underlying Zephyr object. `Thing` will usually be a struct with a
102
+ /// single field, which is a `*mut kobj`.
103
+ ///
47
104
/// TODO: Can we avoid the public fields with a const new method?
48
105
///
49
106
/// TODO: Handling const-defined alignment for these.
@@ -56,9 +113,23 @@ pub struct StaticKernelObject<T> {
56
113
pub init : AtomicUsize ,
57
114
}
58
115
59
- /// Each can be wrapped appropriately. The wrapped type is the instance that holds the raw pointer.
116
+ /// Define the Wrapping of a kernel object.
117
+ ///
118
+ /// This trait defines the association between a static kernel object and the two associated Rust
119
+ /// types: `StaticThing` and `Thing`. In the general case: there should be:
120
+ /// ```
121
+ /// impl Wrapped for StaticKernelObject<kobj> {
122
+ /// type T = Thing,
123
+ /// type I = (),
124
+ /// fn get_wrapped(&self, args: Self::I) -> Self::T {
125
+ /// let ptr = self.value.get();
126
+ /// // Initizlie the kobj using ptr and possible the args.
127
+ /// Thing { ptr }
128
+ /// }
129
+ /// }
130
+ /// ```
60
131
pub trait Wrapped {
61
- /// The wrapped type. This is what `take ()` on the StaticKernelObject will return after
132
+ /// The wrapped type. This is what `init_once ()` on the StaticKernelObject will return after
62
133
/// initialization.
63
134
type T ;
64
135
88
159
StaticKernelObject < T > : Wrapped ,
89
160
{
90
161
/// Construct an empty of these objects, with the zephyr data zero-filled. This is safe in the
91
- /// sense that Zephyr we track the initialization, and they start in the uninitialized state.
162
+ /// sense that Zephyr we track the initialization, they start in the uninitialized state, and
163
+ /// the zero value of the initialize atomic indicates that it is uninitialized.
92
164
pub const fn new ( ) -> StaticKernelObject < T > {
93
165
StaticKernelObject {
94
166
value : unsafe { mem:: zeroed ( ) } ,
@@ -100,6 +172,8 @@ where
100
172
///
101
173
/// Will return a single wrapped instance of this object. This will invoke the initialization,
102
174
/// and return `Some<Wrapped>` for the wrapped containment type.
175
+ ///
176
+ /// If it is called an additional time, it will return None.
103
177
pub fn init_once ( & self , args : <Self as Wrapped >:: I ) -> Option < <Self as Wrapped >:: T > {
104
178
if let Err ( _) = self . init . compare_exchange (
105
179
KOBJ_UNINITIALIZED ,
@@ -225,6 +299,13 @@ macro_rules! _kobj_stack {
225
299
}
226
300
} ;
227
301
302
+ // This initializer needs to have the elements of the array initialized to fixed elements of the
303
+ // `RealStaticThreadStack`. Unfortunately, methods such as [`each_ref`] on the array are not
304
+ // const and can't be used in a static initializer. We could use a recursive macro definition
305
+ // to perform the initialization, but this would require the array size to only be an integer
306
+ // literal (constants aren't calculated until after macro expansion). It may also be possible
307
+ // to write a constructor for the array as a const fn, which would greatly simplify the
308
+ // initialization here.
228
309
( $v: vis, $name: ident, $size: expr, $asize: expr) => {
229
310
compile_error!( "TODO: Stack initializer array" ) ;
230
311
}
0 commit comments