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
//! 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.
///
/// `TlsfHeap::init` is `unsafe` precisely because calling it twice hands
/// the allocator a region it has already given parts of away, after which
/// two live allocations can overlap. That is not a failure anything
/// reports — it is silent memory corruption some distance from its cause.
/// Guarding it here is what lets [`init`] be a safe function at all.
static INITIALIZED: AtomicBool = 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.