1use core::{
4 alloc::{GlobalAlloc, Layout},
5 ptr::NonNull,
6};
7
8use ax_kspin::SpinNoIrq;
9use rlsf::Tlsf;
10
11use super::{AllocResult, AllocatorOps, UsageKind, Usages};
12
13#[cfg_attr(all(target_os = "none", not(test)), global_allocator)]
15static GLOBAL_ALLOCATOR: GlobalAllocator = GlobalAllocator::new();
16
17const PAGE_SIZE: usize = 0x1000;
18
19pub type DefaultByteAllocator = Tlsf<'static, u32, u32, 28, 32>;
21
22struct TlsfInfo {
23 tlsf: Tlsf<'static, u32, u32, 28, 32>,
24 total_bytes: usize,
25 used_bytes: usize,
26}
27
28impl TlsfInfo {
29 const fn new() -> Self {
30 Self {
31 tlsf: Tlsf::new(),
32 total_bytes: 0,
33 used_bytes: 0,
34 }
35 }
36}
37
38pub struct GlobalAllocator {
40 inner: SpinNoIrq<TlsfInfo>,
41 usages: SpinNoIrq<Usages>,
42}
43
44impl Default for GlobalAllocator {
45 fn default() -> Self {
46 Self::new()
47 }
48}
49
50impl GlobalAllocator {
51 pub const fn new() -> Self {
53 Self {
54 inner: SpinNoIrq::new(TlsfInfo::new()),
55 usages: SpinNoIrq::new(Usages::new()),
56 }
57 }
58
59 pub const fn name(&self) -> &'static str {
61 "TLSF"
62 }
63
64 pub fn init(&self, start_vaddr: usize, size: usize) -> AllocResult {
66 let mut inner = self.inner.lock();
67 unsafe {
68 let pool = core::slice::from_raw_parts_mut(start_vaddr as *mut u8, size);
69 inner
70 .tlsf
71 .insert_free_block_ptr(NonNull::new(pool).unwrap())
72 .unwrap();
73 }
74 inner.total_bytes = size;
75 Ok(())
76 }
77
78 pub fn add_memory(&self, start_vaddr: usize, size: usize) -> AllocResult {
80 let mut inner = self.inner.lock();
81 unsafe {
82 let pool = core::slice::from_raw_parts_mut(start_vaddr as *mut u8, size);
83 inner
84 .tlsf
85 .insert_free_block_ptr(NonNull::new(pool).unwrap())
86 .ok_or(crate::AllocError::InvalidParam)?;
87 }
88 inner.total_bytes += size;
89 Ok(())
90 }
91
92 pub fn alloc(&self, layout: Layout) -> AllocResult<NonNull<u8>> {
94 let ptr = self
95 .inner
96 .lock()
97 .tlsf
98 .allocate(layout)
99 .ok_or(crate::AllocError::NoMemory)?;
100 self.inner.lock().used_bytes += layout.size();
101 self.usages.lock().alloc(UsageKind::RustHeap, layout.size());
102 Ok(ptr)
103 }
104
105 pub fn dealloc(&self, pos: NonNull<u8>, layout: Layout) {
107 unsafe {
108 self.inner.lock().tlsf.deallocate(pos, layout.align());
109 }
110 self.inner.lock().used_bytes -= layout.size();
111 self.usages
112 .lock()
113 .dealloc(UsageKind::RustHeap, layout.size());
114 }
115
116 pub fn alloc_pages(
118 &self,
119 num_pages: usize,
120 alignment: usize,
121 kind: UsageKind,
122 ) -> AllocResult<usize> {
123 let size = num_pages * PAGE_SIZE;
124 let align = alignment.max(PAGE_SIZE);
125 let layout =
126 Layout::from_size_align(size, align).map_err(|_| crate::AllocError::InvalidParam)?;
127 let ptr = self
128 .inner
129 .lock()
130 .tlsf
131 .allocate(layout)
132 .ok_or(crate::AllocError::NoMemory)?;
133 self.inner.lock().used_bytes += size;
134 if !matches!(kind, UsageKind::RustHeap) {
135 self.usages.lock().alloc(kind, size);
136 }
137 Ok(ptr.as_ptr() as usize)
138 }
139
140 pub fn alloc_dma32_pages(
142 &self,
143 _num_pages: usize,
144 _alignment: usize,
145 _kind: UsageKind,
146 ) -> AllocResult<usize> {
147 unimplemented!("TLSF allocator does not support alloc_dma32_pages")
148 }
149
150 pub fn alloc_pages_at(
152 &self,
153 _start: usize,
154 _num_pages: usize,
155 _alignment: usize,
156 _kind: UsageKind,
157 ) -> AllocResult<usize> {
158 unimplemented!("TLSF allocator does not support alloc_pages_at")
159 }
160
161 pub fn dealloc_pages(&self, pos: usize, num_pages: usize, kind: UsageKind) {
163 let size = num_pages * PAGE_SIZE;
164 let ptr = NonNull::new(pos as *mut u8).expect("dealloc_pages null ptr");
165 unsafe {
166 self.inner.lock().tlsf.deallocate(ptr, PAGE_SIZE);
167 }
168 self.inner.lock().used_bytes -= size;
169 self.usages.lock().dealloc(kind, size);
170 }
171
172 pub fn used_bytes(&self) -> usize {
174 self.inner.lock().used_bytes
175 }
176
177 pub fn available_bytes(&self) -> usize {
179 let inner = self.inner.lock();
180 inner.total_bytes.saturating_sub(inner.used_bytes)
181 }
182
183 pub fn used_pages(&self) -> usize {
185 self.used_bytes() / PAGE_SIZE
186 }
187
188 pub fn available_pages(&self) -> usize {
190 self.available_bytes() / PAGE_SIZE
191 }
192
193 pub fn usages(&self) -> Usages {
195 *self.usages.lock()
196 }
197}
198
199impl AllocatorOps for GlobalAllocator {
200 fn name(&self) -> &'static str {
201 GlobalAllocator::name(self)
202 }
203
204 fn init(&self, start_vaddr: usize, size: usize) -> AllocResult {
205 GlobalAllocator::init(self, start_vaddr, size)
206 }
207
208 fn add_memory(&self, start_vaddr: usize, size: usize) -> AllocResult {
209 GlobalAllocator::add_memory(self, start_vaddr, size)
210 }
211
212 fn alloc(&self, layout: Layout) -> AllocResult<NonNull<u8>> {
213 GlobalAllocator::alloc(self, layout)
214 }
215
216 fn dealloc(&self, pos: NonNull<u8>, layout: Layout) {
217 GlobalAllocator::dealloc(self, pos, layout)
218 }
219
220 fn alloc_pages(
221 &self,
222 num_pages: usize,
223 alignment: usize,
224 kind: UsageKind,
225 ) -> AllocResult<usize> {
226 GlobalAllocator::alloc_pages(self, num_pages, alignment, kind)
227 }
228
229 fn alloc_dma32_pages(
230 &self,
231 num_pages: usize,
232 alignment: usize,
233 kind: UsageKind,
234 ) -> AllocResult<usize> {
235 GlobalAllocator::alloc_dma32_pages(self, num_pages, alignment, kind)
236 }
237
238 fn alloc_pages_at(
239 &self,
240 start: usize,
241 num_pages: usize,
242 alignment: usize,
243 kind: UsageKind,
244 ) -> AllocResult<usize> {
245 GlobalAllocator::alloc_pages_at(self, start, num_pages, alignment, kind)
246 }
247
248 fn dealloc_pages(&self, pos: usize, num_pages: usize, kind: UsageKind) {
249 GlobalAllocator::dealloc_pages(self, pos, num_pages, kind)
250 }
251
252 fn used_bytes(&self) -> usize {
253 GlobalAllocator::used_bytes(self)
254 }
255
256 fn available_bytes(&self) -> usize {
257 GlobalAllocator::available_bytes(self)
258 }
259
260 fn used_pages(&self) -> usize {
261 GlobalAllocator::used_pages(self)
262 }
263
264 fn available_pages(&self) -> usize {
265 GlobalAllocator::available_pages(self)
266 }
267
268 fn usages(&self) -> Usages {
269 GlobalAllocator::usages(self)
270 }
271}
272
273pub fn global_allocator() -> &'static GlobalAllocator {
275 &GLOBAL_ALLOCATOR
276}
277
278pub fn init_percpu_slab(_cpu_id: usize) {}
282
283pub fn global_init(start_vaddr: usize, size: usize) -> AllocResult {
285 debug!(
286 "initialize global allocator at: [{:#x}, {:#x})",
287 start_vaddr,
288 start_vaddr + size
289 );
290 GLOBAL_ALLOCATOR.init(start_vaddr, size)
291}
292
293pub fn global_add_memory(start_vaddr: usize, size: usize) -> AllocResult {
295 debug!(
296 "add a memory region to global allocator: [{:#x}, {:#x})",
297 start_vaddr,
298 start_vaddr + size
299 );
300 GLOBAL_ALLOCATOR.add_memory(start_vaddr, size)
301}
302
303unsafe impl GlobalAlloc for GlobalAllocator {
304 unsafe fn alloc(&self, layout: Layout) -> *mut u8 {
305 let inner = move || {
306 if let Ok(ptr) = GlobalAllocator::alloc(self, layout) {
307 ptr.as_ptr()
308 } else {
309 alloc::alloc::handle_alloc_error(layout)
310 }
311 };
312
313 #[cfg(feature = "tracking")]
314 {
315 crate::tracking::with_state(|state| match state {
316 None => inner(),
317 Some(state) => {
318 let ptr = inner();
319 let generation = state.generation;
320 state.generation += 1;
321 state.map.insert(
322 ptr as usize,
323 crate::tracking::AllocationInfo {
324 layout,
325 backtrace: axbacktrace::Backtrace::capture(),
326 generation,
327 },
328 );
329 ptr
330 }
331 })
332 }
333
334 #[cfg(not(feature = "tracking"))]
335 inner()
336 }
337
338 unsafe fn dealloc(&self, ptr: *mut u8, layout: Layout) {
339 let ptr = NonNull::new(ptr).expect("dealloc null ptr");
340 let inner = || GlobalAllocator::dealloc(self, ptr, layout);
341
342 #[cfg(feature = "tracking")]
343 crate::tracking::with_state(|state| match state {
344 None => inner(),
345 Some(state) => {
346 let address = ptr.as_ptr() as usize;
347 state.map.remove(&address);
348 inner()
349 }
350 });
351
352 #[cfg(not(feature = "tracking"))]
353 inner();
354 }
355}