1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
//! The global heap.
//!
//! Most bare-metal firmware has no heap at all, and a board reaching for
//! this one usually has a specific reason. `rustls` refuses to build
//! without `alloc` and allocates freely while it parses and validates a
//! certificate chain; `resident-fat` keeps the allocation table and every
//! directory it has read in memory, which is the whole reason it can move
//! a file in one transfer instead of a command per block. Both are
//! deliberate trades of memory for capability, and both are good trades
//! on a board with half a gigabyte of it.
//!
//! ```ignore
//! let bytes = heap::init(&mut mailbox)?;
//! logln!("heap: {} MiB", bytes / (1024 * 1024));
//! ```
//!
//! # Installed by linkage
//!
//! This module declares the `#[global_allocator]`, which is a decision for
//! the whole program rather than for whatever asked: a binary may contain
//! exactly one, and every crate in it allocates through this one.
//!
//! That is why it is behind a feature, off by default, the same as
//! [`crate::entropy`]'s `getrandom` backend. A board that wants
//! a different allocator — or a different region, or no heap at all —
//! leaves the feature alone and declares its own, and nothing here
//! competes with it. There is no way to make such a choice overridable;
//! the only honest thing a library can do is make taking it explicit.
//!
//! # Why TLSF rather than a linked list
//!
//! `embedded-alloc` offers both. A linked-list first-fit allocator is
//! simpler, but its allocation cost grows with the number of free blocks
//! and it fragments under repeated mixed-size allocate and free — which
//! is exactly the workload here, since every TLS handshake allocates and
//! frees a certificate chain. TLSF is bounded time for both operations
//! and resists fragmentation by design, which is what a board expected to
//! run for months without a power cycle wants: the cost of being wrong
//! about it is a device that gets slower and then stops, long after
//! anyone is watching the console.
use ;
use TlsfHeap;
use Mailbox;
use mem;
/// The heap itself.
///
/// `empty()` is a `const` constructor, so this can be a `static` — but it
/// hands out nothing until [`init`] gives it a region, and any allocation
/// before that point panics.
static HEAP: TlsfHeap = empty;
/// Whether [`init`] has run, so that a second call reports rather than
/// corrupts.
///
/// A second `TlsfHeap::init` would hand the allocator a region it has
/// already given parts of away, after which two live allocations could
/// overlap. `embedded-alloc` 0.7 panics rather than let that happen — 0.6
/// let it, silently — and guarding it here turns the panic into
/// [`Error::AlreadyInitialized`], which a board can report and carry on
/// from.
static INITIALIZED: AtomicBool = new;
/// The region [`init`] gave the allocator, in bytes; zero before then.
/// What [`usage`] reckons `used` against.
static SIZE: AtomicUsize = new;
/// Why [`init`] could not hand the allocator a region.
/// Gives the allocator every byte between the end of the image and the
/// top of the ARM's share of RAM, returning the size of the region.
///
/// Call before anything allocates — an allocation before this point
/// panics, and on a board with no console yet that is a silent stop.
///
/// The bounds come from `rpi-hal`, which asks the firmware rather than
/// hardcoding them, so the same image sizes its heap correctly whatever
/// `gpu_mem` the board is set to and however much RAM it has.
///
/// # Errors
///
/// [`Error::Region`] if the firmware does not answer or leaves no room,
/// and [`Error::AlreadyInitialized`] on a second call.
/// How the heap's bytes are split, as [`usage`] found them.
/// The heap's usage right now; both zero before [`init`].
///
/// Walks every block in the heap inside a critical section — interrupts
/// masked, and on a multicore board the other cores held off it — so the
/// cost grows with how many blocks there are, live and free, rather than
/// with the heap's size. Cheap enough for a metrics scrape every few
/// seconds; not something to call in a loop.