blob: c8b76ac6d0d88d02bba7b10d691aba968ece231a [file] [log] [blame]
Pierre-Clément Tosifc531152022-10-20 12:22:23 +01001// Copyright 2022, The Android Open Source Project
2//
3// Licensed under the Apache License, Version 2.0 (the "License");
4// you may not use this file except in compliance with the License.
5// You may obtain a copy of the License at
6//
7// http://www.apache.org/licenses/LICENSE-2.0
8//
9// Unless required by applicable law or agreed to in writing, software
10// distributed under the License is distributed on an "AS IS" BASIS,
11// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12// See the License for the specific language governing permissions and
13// limitations under the License.
14
15//! Heap implementation.
16
Pierre-Clément Tosidb74cb12022-12-08 13:56:25 +000017use alloc::alloc::alloc;
18use alloc::alloc::Layout;
19use alloc::boxed::Box;
20
Pierre-Clément Tosi54e71d02022-12-08 13:57:43 +000021use core::alloc::GlobalAlloc as _;
Pierre-Clément Tosi54e71d02022-12-08 13:57:43 +000022use core::ffi::c_void;
23use core::mem;
24use core::num::NonZeroUsize;
25use core::ptr;
26use core::ptr::NonNull;
27
Pierre-Clément Tosifc531152022-10-20 12:22:23 +010028use buddy_system_allocator::LockedHeap;
29
Pierre-Clément Tosif3681e82023-06-22 11:38:22 +000030/// Configures the size of the global allocator.
31#[macro_export]
Pierre-Clément Tosi6a4808c2023-06-29 09:19:38 +000032macro_rules! configure_heap {
Pierre-Clément Tosif3681e82023-06-22 11:38:22 +000033 ($len:expr) => {
34 static mut __HEAP_ARRAY: [u8; $len] = [0; $len];
35 #[export_name = "HEAP"]
Andrew Walbranc06e7342023-07-05 14:00:51 +000036 // SAFETY: HEAP will only be accessed once as mut, from init().
Pierre-Clément Tosif3681e82023-06-22 11:38:22 +000037 static mut __HEAP: &'static mut [u8] = unsafe { &mut __HEAP_ARRAY };
38 };
39}
40
41extern "Rust" {
Pierre-Clément Tosi6a4808c2023-06-29 09:19:38 +000042 /// Slice used by the global allocator, configured using configure_heap!().
Pierre-Clément Tosif3681e82023-06-22 11:38:22 +000043 static mut HEAP: &'static mut [u8];
44}
Pierre-Clément Tosifc531152022-10-20 12:22:23 +010045
Alan Stokesa0e42962023-04-14 17:59:50 +010046#[global_allocator]
47static HEAP_ALLOCATOR: LockedHeap<32> = LockedHeap::<32>::new();
48
Pierre-Clément Tosif3681e82023-06-22 11:38:22 +000049/// Initialize the global allocator.
50///
51/// # Safety
52///
53/// Must be called no more than once.
Pierre-Clément Tosi8ca7a442023-06-22 13:47:15 +000054pub(crate) unsafe fn init() {
Alan Stokesa0e42962023-04-14 17:59:50 +010055 // SAFETY: Nothing else accesses this memory, and we hand it over to the heap to manage and
56 // never touch it again. The heap is locked, so there cannot be any races.
57 let (start, size) = unsafe { (HEAP.as_mut_ptr() as usize, HEAP.len()) };
58
59 let mut heap = HEAP_ALLOCATOR.lock();
60 // SAFETY: We are supplying a valid memory range, and we only do this once.
61 unsafe { heap.init(start, size) };
Pierre-Clément Tosifc531152022-10-20 12:22:23 +010062}
Pierre-Clément Tosi54e71d02022-12-08 13:57:43 +000063
Pierre-Clément Tosidb74cb12022-12-08 13:56:25 +000064/// Allocate an aligned but uninitialized slice of heap.
65pub fn aligned_boxed_slice(size: usize, align: usize) -> Option<Box<[u8]>> {
66 let size = NonZeroUsize::new(size)?.get();
67 let layout = Layout::from_size_align(size, align).ok()?;
Andrew Walbranc06e7342023-07-05 14:00:51 +000068 // SAFETY: We verify that `size` and the returned `ptr` are non-null.
Pierre-Clément Tosidb74cb12022-12-08 13:56:25 +000069 let ptr = unsafe { alloc(layout) };
70 let ptr = NonNull::new(ptr)?.as_ptr();
71 let slice_ptr = ptr::slice_from_raw_parts_mut(ptr, size);
72
Andrew Walbranc06e7342023-07-05 14:00:51 +000073 // SAFETY: The memory was allocated using the proper layout by our global_allocator.
Pierre-Clément Tosidb74cb12022-12-08 13:56:25 +000074 Some(unsafe { Box::from_raw(slice_ptr) })
75}
76
Pierre-Clément Tosi54e71d02022-12-08 13:57:43 +000077#[no_mangle]
78unsafe extern "C" fn malloc(size: usize) -> *mut c_void {
Alan Stokesa0e42962023-04-14 17:59:50 +010079 allocate(size, false).map_or(ptr::null_mut(), |p| p.cast::<c_void>().as_ptr())
Pierre-Clément Tosiaaa08692023-03-10 13:55:19 +000080}
81
82#[no_mangle]
83unsafe extern "C" fn calloc(nmemb: usize, size: usize) -> *mut c_void {
84 let Some(size) = nmemb.checked_mul(size) else {
85 return ptr::null_mut()
86 };
Alan Stokesa0e42962023-04-14 17:59:50 +010087 allocate(size, true).map_or(ptr::null_mut(), |p| p.cast::<c_void>().as_ptr())
Pierre-Clément Tosi54e71d02022-12-08 13:57:43 +000088}
89
90#[no_mangle]
Alan Stokesa0e42962023-04-14 17:59:50 +010091/// SAFETY: ptr must be null or point to a currently-allocated block returned by allocate (either
92/// directly or via malloc or calloc). Note that this function is called directly from C, so we have
93/// to trust that the C code is doing the right thing; there are checks below which will catch some
94/// errors.
Pierre-Clément Tosi54e71d02022-12-08 13:57:43 +000095unsafe extern "C" fn free(ptr: *mut c_void) {
Alan Stokesa0e42962023-04-14 17:59:50 +010096 let Some(ptr) = NonNull::new(ptr) else { return };
97 // SAFETY: The contents of the HEAP slice may change, but the address range never does.
98 let heap_range = unsafe { HEAP.as_ptr_range() };
99 assert!(
100 heap_range.contains(&(ptr.as_ptr() as *const u8)),
101 "free() called on a pointer that is not part of the HEAP: {ptr:?}"
102 );
Andrew Walbranc06e7342023-07-05 14:00:51 +0000103 // SAFETY: ptr is non-null and was allocated by allocate, which prepends a correctly aligned
104 // usize.
Alan Stokesa0e42962023-04-14 17:59:50 +0100105 let (ptr, size) = unsafe {
Alan Stokesa0e42962023-04-14 17:59:50 +0100106 let ptr = ptr.cast::<usize>().as_ptr().offset(-1);
107 (ptr, *ptr)
108 };
109 let size = NonZeroUsize::new(size).unwrap();
110 let layout = malloc_layout(size).unwrap();
111 // SAFETY: If our precondition is satisfied, then this is a valid currently-allocated block.
112 unsafe { HEAP_ALLOCATOR.dealloc(ptr as *mut u8, layout) }
113}
114
115/// Allocate a block of memory suitable to return from `malloc()` etc. Returns a valid pointer
116/// to a suitable aligned region of size bytes, optionally zeroed (and otherwise uninitialized), or
117/// None if size is 0 or allocation fails. The block can be freed by passing the returned pointer to
118/// `free()`.
119fn allocate(size: usize, zeroed: bool) -> Option<NonNull<usize>> {
120 let size = NonZeroUsize::new(size)?.checked_add(mem::size_of::<usize>())?;
121 let layout = malloc_layout(size)?;
122 // SAFETY: layout is known to have non-zero size.
123 let ptr = unsafe {
124 if zeroed {
125 HEAP_ALLOCATOR.alloc_zeroed(layout)
126 } else {
127 HEAP_ALLOCATOR.alloc(layout)
Pierre-Clément Tosi54e71d02022-12-08 13:57:43 +0000128 }
Alan Stokesa0e42962023-04-14 17:59:50 +0100129 };
130 let ptr = NonNull::new(ptr)?.cast::<usize>().as_ptr();
131 // SAFETY: ptr points to a newly allocated block of memory which is properly aligned
132 // for a usize and is big enough to hold a usize as well as the requested number of
133 // bytes.
134 unsafe {
135 *ptr = size.get();
136 NonNull::new(ptr.offset(1))
Pierre-Clément Tosi54e71d02022-12-08 13:57:43 +0000137 }
138}
139
Alan Stokesa0e42962023-04-14 17:59:50 +0100140fn malloc_layout(size: NonZeroUsize) -> Option<Layout> {
141 // We want at least 8 byte alignment, and we need to be able to store a usize.
142 const ALIGN: usize = const_max_size(mem::size_of::<usize>(), mem::size_of::<u64>());
143 Layout::from_size_align(size.get(), ALIGN).ok()
Pierre-Clément Tosi54e71d02022-12-08 13:57:43 +0000144}
145
Alan Stokesa0e42962023-04-14 17:59:50 +0100146const fn const_max_size(a: usize, b: usize) -> usize {
147 if a > b {
148 a
149 } else {
150 b
151 }
Pierre-Clément Tosi54e71d02022-12-08 13:57:43 +0000152}