base/alloc/
heap.rs

1pub static HEAP: Mutex<Option<Heap<32>>> = Mutex::new(None);
2
3pub const HEAP_START: usize = 0x4444_4444_0000;
4pub const HEAP_SIZE: usize = 100 * 1024;
5
6pub struct Heap<const ORDER: usize> {
7   pub allocated: usize,
8   pub freeList: [LinkedList; ORDER],
9   pub total: usize,
10   pub user: usize,
11}
12
13impl<const ORDER: usize> Heap<ORDER> {
14   /// Create an empty heap.
15   pub const fn new() -> Self {
16      return Heap{
17         allocated: 0,
18         freeList: [LinkedList::new(); ORDER],
19         total: 0,
20         user: 0,
21      };
22   }
23
24   pub unsafe fn add_to_heap(&mut self, mut start: usize, mut end: usize) {
25      // Avoid unaligned access.
26      start = (start + size_of::<usize>() - 1) & (!size_of::<usize>() + 1);
27      end &= !size_of::<usize>() + 1;
28      assert!(start <= end);
29
30      let mut total = 0;
31      let mut current_start = start;
32
33      while current_start + size_of::<usize>() <= end {
34         let lowbit = current_start & (!current_start + 1);
35         let size = min(lowbit, previous_po2(end - current_start));
36         total += size;
37
38         self.freeList[size.trailing_zeros() as usize].push(current_start as *mut usize);
39         current_start += size;
40      }
41
42      self.total += total;
43   }
44
45   /// Allocate a block of memory large enough to contain `size` bytes,
46   /// and aligned on `align`.  This will return NULL if the `align` is
47   /// greater than `MIN_HEAP_ALIGN`, if `align` is not a power of 2, or
48   /// if we can't find enough memory.
49   ///
50   /// All allocated memory must be passed to `deallocate` with the same
51   /// `size` and `align` parameter, or else horrible things will happen.
52   pub unsafe fn allocate(&mut self, layout: Layout) -> Result<NonNull<u8>, AllocationError> {
53      let size = max(
54         layout.size.nextPowerOf2(),
55         max(layout.align, size_of::<usize>()),
56      );
57
58      let class = size.trailing_zeros() as usize;
59      for i in class..self.freeList.len() {
60         // Find the first non-empty size class
61         if !self.freeList[i].empty() {
62            // Split the buffers.
63            for j in (class + 1..i + 1).rev() {
64               if let Some(block) = self.freeList[j].pop() {
65                  unsafe {
66                     self.freeList[j - 1].push((block as usize + (1 << (j - 1))) as *mut usize);
67                     self.freeList[j - 1].push(block);
68                  }
69               } else {
70                  return Err(AllocationError);
71               }
72            }
73
74            let result = NonNull::new(
75               self.freeList[class].pop()
76                  .expect("current block should have free space") as *mut u8
77            );
78
79            return if let Some(result) = result {
80               self.user += layout.size;
81               self.allocated += size;
82               Ok(result)
83            } else {
84               Err(AllocationError)
85            };
86         }
87      }
88
89      return Err(AllocationError);
90   }
91
92   pub unsafe fn deallocate(&mut self, ptr: NonNull<u8>, layout: Layout) {
93      let size = max(
94         layout.size.nextPowerOf2(),
95         max(layout.align, size_of::<usize>()),
96      );
97
98      let class = size.trailing_zeros() as usize;
99
100      // Place the block back into the free list.
101      self.freeList[class].push(ptr.as_ptr() as *mut usize);
102
103      // Merge buddy free lists.
104      let mut currentPointer = ptr.as_ptr() as usize;
105      let mut currentClass = class;
106
107      while currentClass < self.freeList.len() {
108         let buddy = currentPointer ^ (1 << currentClass);
109         let mut flag = false;
110
111         for block in self.freeList[currentClass].iterator_mut() {
112            if block.value() as usize == buddy {
113               block.pop();
114               flag = true;
115               break;
116            }
117         }
118
119         if flag {
120            self.freeList[currentClass].pop();
121            currentPointer = min(currentPointer, buddy);
122            currentClass += 1;
123            self.freeList[currentClass].push(currentPointer as *mut usize);
124         } else {
125            break;
126         }
127      }
128
129      self.user -= layout.size;
130      self.allocated -= size;
131   }
132}
133
134impl<const ORDER: usize> Debug for Heap<ORDER> {
135   fn fmt(&self, f: &mut Formatter<'_>) -> FmtResult {
136      f.debug_struct("Heap")
137         .field("user", &self.user)
138         .field("allocated", &self.allocated)
139         .field("total", &self.total)
140         .finish()
141   }
142}
143
144/// A locked version of `Heap`
145///
146/// # Usage
147///
148/// Create a locked heap and add a memory region to it:
149/// ```
150/// use base::alloc::heap::*;
151/// # use core::mem::size_of;
152/// let mut heap = LockedHeap::<32>::new();
153/// # let space: [usize; 100] = [0; 100];
154/// # let begin: usize = space.as_ptr() as usize;
155/// # let end: usize = begin + 100 * size_of::<usize>();
156/// # let size: usize = 100 * size_of::<usize>();
157/// unsafe {
158///     heap.lock().init(begin, size);
159///     // or
160///     heap.lock().add_to_heap(begin, end);
161/// }
162/// ```
163pub struct LockedHeap<const ORDER: usize>(Mutex<Heap<ORDER>>);
164
165impl<const ORDER: usize> LockedHeap<ORDER> {
166   pub const fn new() -> Self {
167      return LockedHeap(Mutex::new(Heap::<ORDER>::new()));
168   }
169}
170
171unsafe impl<const ORDER: usize> GlobalAlloc for LockedHeap<ORDER> {
172   unsafe fn alloc(&self, layout: StdLayout) -> *mut u8 {
173      let layout = Layout::from(layout);
174
175      self.0.lock()
176         .allocate(layout)
177         .ok()
178         .map_or(0 as *mut u8, |allocation| allocation.as_ptr())
179   }
180
181   unsafe fn dealloc(&self, ptr: *mut u8, layout: StdLayout) {
182      let layout = Layout::from(layout);
183      self.0.lock().deallocate(NonNull::new_unchecked(ptr), layout);
184   }
185}
186
187// IMPORTS //
188
189use {
190   crate::{
191      alloc::{AllocationError, Layout},
192      array::linked_list::LinkedList,
193      math::{previous_po2, PowersOf2},
194   },
195   std_alloc::alloc::{GlobalAlloc, Layout as StdLayout},
196   core::{
197      cmp::{min, max},
198      fmt::{Debug, Formatter, Result as FmtResult},
199      mem::size_of,
200      ops::Deref,
201      ptr::NonNull,
202   },
203   spin::Mutex,
204   x86_64::{
205      structures::paging::{
206         mapper::MapToError, FrameAllocator, Mapper, Page, PageTableFlags, Size4KiB,
207      },
208      VirtAddr,
209   },
210};