kernel/utilities/
static_init.rs

1// Licensed under the Apache License, Version 2.0 or the MIT License.
2// SPDX-License-Identifier: Apache-2.0 OR MIT
3// Copyright Tock Contributors 2022.
4
5//! Support for statically initializing objects in memory.
6
7/// Allocates a statically-sized global array of memory and initializes the
8/// memory for a particular data structure.
9///
10/// This macro creates the static buffer, ensures it is initialized to the
11/// proper type, and then returns a `&'static mut` reference to it.
12///
13/// Note: Because this instantiates a static object, you generally cannot pass
14/// a type with generic parameters. github.com/tock/tock/issues/2995 for detail.
15///
16/// # Safety
17///
18/// As this macro will write directly to a global area without acquiring a lock
19/// or similar, calling this macro is inherently unsafe. The caller should take
20/// care to never call the code that initializes this buffer twice, as doing so
21/// will overwrite the value from first allocation without running its
22/// destructor.
23#[macro_export]
24macro_rules! static_init {
25    ($T:ty, $e:expr $(,)?) => {{
26        let mut buf = $crate::static_buf!($T);
27        buf.write($e)
28    }};
29}
30
31/// Internal helper function for [`static_buf!()`](crate::static_buf).
32///
33/// This must be public to work within the macro but should never be used
34/// directly.
35///
36/// This is a `#[inline(never)]` function that panics internally if the passed
37/// reference is `true`. This function is intended for use within the
38/// [`static_buf!()`](crate::static_buf) macro to detect multiple uses of the
39/// macro on the same buffer.
40///
41/// This function is implemented separately without inlining to removes the size
42/// bloat of track_caller saving the location of every single call to
43/// [`static_buf!()`](crate::static_buf) or
44/// [`static_init!()`](crate::static_init).
45///
46/// If Tock panics with this message:
47///
48/// ```text
49/// Error! Single static_buf!() called twice.
50/// ```
51///
52/// then you have tried to initialize the same static memory twice. This is
53/// prohibited because you would have multiple mutable references to the same
54/// memory. This is likely due to either calling
55/// [`static_buf!()`](crate::static_buf) in a loop or calling a function
56/// multiple times which internally contains a call to
57/// [`static_buf!()`](crate::static_buf). Typically, calls to
58/// [`static_buf!()`](crate::static_buf) are hidden within calls to
59/// [`static_init!()`](crate::static_init) or component helper macros, so start
60/// your search there.
61#[inline(never)]
62pub fn static_buf_check_used(used: &mut bool) {
63    // Check if this `BUF` has already been declared and initialized. If it
64    // has, then this is a repeated `static_buf!()` call which is an error
65    // as it will alias the same `BUF`.
66    if *used {
67        // panic, this buf has already been declared and initialized.
68        // NOTE: To save 144 bytes of code size, use loop {} instead of this
69        // panic.
70        panic!("Error! Single static_buf!() called twice.");
71    } else {
72        // Otherwise, mark our uninitialized buffer as used.
73        *used = true;
74    }
75}
76
77/// Allocates an uninitialized statically-sized global region of memory for a
78/// data structure.
79///
80/// This macro allocates the static memory but does not initialize the memory.
81/// This also checks that the buffer is not aliased and is only used once.
82///
83/// This macro creates the static buffer, and returns a
84/// [`core::mem::MaybeUninit`] wrapper containing the buffer. The memory is
85/// allocated, but it is guaranteed to be uninitialized inside of the wrapper.
86///
87/// Before the static buffer can be used it must be initialized. For example:
88///
89/// ```ignore
90/// let mut static_buffer = static_buf!(T);
91/// let static_reference: &'static mut T = static_buffer.initialize(T::new());
92/// ```
93///
94/// Separating the creation of the static buffer into its own macro is not
95/// strictly necessary, but it allows for more flexibility in Rust when boards
96/// are initialized and the static structures are being created. Since creating
97/// and initializing static buffers requires knowing the particular types (and
98/// their sizes), writing shared initialization code (in components for example)
99/// where the types are unknown since they vary across boards is difficult. By
100/// splitting buffer creating from initialization, creating shared components is
101/// possible.
102#[macro_export]
103macro_rules! static_buf {
104    ($T:ty $(,)?) => {{
105        // Statically allocate a read-write buffer for the value without
106        // actually writing anything, as well as a flag to track if
107        // this memory has been initialized yet.
108        static mut BUF: (core::mem::MaybeUninit<$T>, bool) =
109            (core::mem::MaybeUninit::uninit(), false);
110
111        // To minimize the amount of code duplicated across every invocation
112        // of this macro, all of the logic for checking if the buffer has been
113        // used is contained within the static_buf_check_used function,
114        // which panics if the passed boolean has been used and sets the
115        // boolean to true otherwise.
116        $crate::utilities::static_init::static_buf_check_used(&mut BUF.1);
117
118        // If we get to this point we can wrap our buffer to be eventually
119        // initialized.
120        &mut BUF.0
121    }};
122}
123
124/// A version of [`static_buf!()`] that adds an exported name to the buffer.
125///
126/// This creates a static buffer exactly as [`static_buf!()`] does. In general,
127/// most uses should use [`static_buf!()`]. However, in cases where the symbol
128/// name of the buffer matters, this version is useful.
129///
130/// Allocates a statically-sized global region of memory for data structures but
131/// does not initialize the memory. Checks that the buffer is not aliased and is
132/// only used once.
133///
134/// This macro creates the static buffer, and returns a
135/// [`core::mem::MaybeUninit`] wrapper containing the buffer. The memory is
136/// allocated, but it is guaranteed to be uninitialized inside of the wrapper.
137///
138/// Before the static buffer can be used it must be initialized. For example:
139///
140/// ```ignore
141/// let mut static_buffer = static_nmaed_buf!(T, "MY_BUF");
142/// let static_reference: &'static mut T = static_buffer.initialize(T::new());
143/// ```
144///
145/// Separating the creation of the static buffer into its own macro is not
146/// strictly necessary, but it allows for more flexibility in Rust when boards
147/// are initialized and the static structures are being created. Since creating
148/// and initializing static buffers requires knowing the particular types (and
149/// their sizes), writing shared initialization code (in components for example)
150/// where the types are unknown since they vary across boards is difficult. By
151/// splitting buffer creating from initialization, creating shared components is
152/// possible.
153#[macro_export]
154macro_rules! static_named_buf {
155    ($T:ty, $N:expr $(,)?) => {{
156        // Statically allocate a read-write buffer for the value without
157        // actually writing anything, as well as a flag to track if
158        // this memory has been initialized yet.
159        #[used]
160        #[no_mangle]
161        #[export_name = $N]
162        pub static mut BUF: (core::mem::MaybeUninit<$T>, bool) =
163            (core::mem::MaybeUninit::uninit(), false);
164
165        // To minimize the amount of code duplicated across every invocation
166        // of this macro, all of the logic for checking if the buffer has been
167        // used is contained within the static_buf_check_used function,
168        // which panics if the passed boolean has been used and sets the
169        // boolean to true otherwise.
170        $crate::utilities::static_init::static_buf_check_used(&mut BUF.1);
171
172        // If we get to this point we can wrap our buffer to be eventually
173        // initialized.
174        &mut BUF.0
175    }};
176}