Skip to content

Commit c66cccc

Browse files
committed
feat: add heap implementation for embedded alloc
1 parent d840e1a commit c66cccc

File tree

4 files changed

+156
-0
lines changed

4 files changed

+156
-0
lines changed

bolos-impl/src/lib.rs

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -36,6 +36,9 @@ pub use bolos_sys::exit_app;
3636
pub use bolos_sys::pic;
3737
pub use bolos_sys::pic::PIC;
3838

39+
pub use bolos_sys::heap;
40+
pub use bolos_sys::heap::Heap;
41+
3942
pub mod nvm;
4043
pub use nvm::NVM;
4144

bolos-sys/Cargo.toml

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,14 @@ edition = "2018"
77
[dependencies]
88
cfg-if = "1.0.0"
99

10+
# Required for the global allocator
11+
critical-section = { version = "1.1.2" }
12+
13+
# Required for LLHeap
14+
linked_list_allocator = { version = "0.10.5", default-features = false }
15+
rlsf = { version = "0.2.1", default-features = false, optional = true }
16+
const-default = { version = "1.0.0", default-features = false, optional = true }
17+
1018
zeroize = { version = "1", default-features = false }
1119

1220
[dev-dependencies]

bolos-sys/src/heap.rs

Lines changed: 144 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,144 @@
1+
// Code taken from
2+
// https://github.com/Zondax/embedded-alloc
3+
// https://crates.io/crates/embedded-alloc/0.5.1
4+
5+
// LLHeap with PIC on RefCell
6+
// This was required to PIC the RefCell on the Heap struct. Otherwise, the app will fail with segmentation fault.
7+
8+
use core::alloc::{GlobalAlloc, Layout};
9+
use core::cell::RefCell;
10+
use core::ptr::{self, NonNull};
11+
12+
use critical_section::Mutex;
13+
use linked_list_allocator::Heap as LLHeap;
14+
15+
use crate::pic::PIC;
16+
17+
/// A linked list first fit heap.
18+
pub struct Heap {
19+
pub heap: Mutex<PIC<RefCell<LLHeap>>>,
20+
}
21+
22+
impl Heap {
23+
/// Create a new UNINITIALIZED heap allocator
24+
///
25+
/// You must initialize this heap using the
26+
/// [`init`](Self::init) method before using the allocator.
27+
pub const fn empty() -> Heap {
28+
Heap {
29+
heap: Mutex::new(PIC::new(RefCell::new(LLHeap::empty()))),
30+
}
31+
}
32+
33+
/// Initializes the heap
34+
///
35+
/// This function must be called BEFORE you run any code that makes use of the
36+
/// allocator.
37+
///
38+
/// `start_addr` is the address where the heap will be located.
39+
///
40+
/// `size` is the size of the heap in bytes.
41+
///
42+
/// Note that:
43+
///
44+
/// - The heap grows "upwards", towards larger addresses. Thus `start_addr` will
45+
/// be the smallest address used.
46+
///
47+
/// - The largest address used is `start_addr + size - 1`, so if `start_addr` is
48+
/// `0x1000` and `size` is `0x30000` then the allocator won't use memory at
49+
/// addresses `0x31000` and larger.
50+
///
51+
/// # Safety
52+
///
53+
/// Obey these or Bad Stuff will happen.
54+
///
55+
/// - This function must be called exactly ONCE.
56+
/// - `size > 0`
57+
pub unsafe fn init(&self, start_addr: usize, size: usize) {
58+
// zlog("-- init 1 --\x00");
59+
critical_section::with(|cs| {
60+
self.heap
61+
.borrow(cs)
62+
.borrow_mut()
63+
.init(start_addr as *mut u8, size);
64+
});
65+
}
66+
67+
/// Returns an estimate of the amount of bytes in use.
68+
pub fn used(&self) -> usize {
69+
// zlog("-- used 1 --\x00");
70+
critical_section::with(|cs| self.heap.borrow(cs).borrow_mut().used())
71+
}
72+
73+
/// Returns an estimate of the amount of bytes available.
74+
pub fn free(&self) -> usize {
75+
// zlog("-- free 1 --\x00");
76+
critical_section::with(|cs| self.heap.borrow(cs).borrow_mut().free())
77+
}
78+
79+
pub fn alloc(&self, layout: Layout) -> Option<NonNull<u8>> {
80+
// zlog("-- alloc 1 --\x00");
81+
let res = critical_section::with(|cs| {
82+
// zlog("-- alloc 2 --\x00");
83+
let heap = &self.heap;
84+
// zlog("-- alloc 3 --\x00");
85+
let heap2 = heap.borrow(cs);
86+
// zlog("-- alloc 4 --\x00");
87+
let mut heap3 = heap2.borrow_mut();
88+
// zlog("-- alloc 5 --\x00");
89+
let result = heap3.allocate_first_fit(layout);
90+
// zlog("-- alloc 6 --\x00");
91+
result.ok()
92+
});
93+
// zlog("-- alloc 7 --\x00");
94+
res
95+
}
96+
97+
pub unsafe fn dealloc(&self, ptr: *mut u8, layout: Layout) {
98+
// zlog("-- dealloc 1 --\x00");
99+
critical_section::with(|cs| {
100+
self.heap
101+
.borrow(cs)
102+
.borrow_mut()
103+
.deallocate(NonNull::new_unchecked(ptr), layout)
104+
});
105+
}
106+
}
107+
108+
unsafe impl GlobalAlloc for PIC<Heap> {
109+
unsafe fn alloc(&self, layout: Layout) -> *mut u8 {
110+
// zlog("-- GlobalAlloc alloc 1 --\x00");
111+
self.get_ref().alloc(layout)
112+
.map_or(ptr::null_mut(), |allocation| allocation.as_ptr())
113+
}
114+
115+
unsafe fn dealloc(&self, ptr: *mut u8, layout: Layout) {
116+
// zlog("-- GlobalAlloc dealloc 1 --\x00");
117+
self.get_ref().dealloc(ptr, layout);
118+
}
119+
}
120+
121+
#[cfg(feature = "allocator_api")]
122+
mod allocator_api {
123+
use super::*;
124+
use core::alloc::{AllocError, Allocator};
125+
126+
unsafe impl Allocator for Heap {
127+
fn allocate(&self, layout: Layout) -> Result<NonNull<[u8]>, AllocError> {
128+
zlog("-- Allocator allocate 1 --\x00");
129+
match layout.size() {
130+
0 => Ok(NonNull::slice_from_raw_parts(layout.dangling(), 0)),
131+
size => self.alloc(layout).map_or(Err(AllocError), |allocation| {
132+
Ok(NonNull::slice_from_raw_parts(allocation, size))
133+
}),
134+
}
135+
}
136+
137+
unsafe fn deallocate(&self, ptr: NonNull<u8>, layout: Layout) {
138+
zlog("-- Allocator deallocate 1 --\x00");
139+
if layout.size() != 0 {
140+
self.dealloc(ptr.as_ptr(), layout);
141+
}
142+
}
143+
}
144+
}

bolos-sys/src/lib.rs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -53,3 +53,4 @@ pub fn exit_app(_status: u8) -> ! {
5353
mod extra_traits;
5454

5555
pub mod pic;
56+
pub mod heap;

0 commit comments

Comments
 (0)