1use crate::{
4 arch::xshim::PhysAddr,
5 boot::entry::{MemoryKind, MemoryRegion},
6 memory::phys_to_virt,
7 serial_println,
8 sync::SpinLock,
9};
10
11const PAGE_SIZE: u64 = 4096;
12pub const MAX_BOOT_ALLOC_REGIONS: usize = 512;
13pub const MAX_PROTECTED_RANGES: usize = 32;
14
15#[derive(Clone, Copy)]
16struct BootRegion {
17 start: u64,
18 end: u64,
19}
20
21impl BootRegion {
22 const fn empty() -> Self {
23 Self { start: 0, end: 0 }
24 }
25
26 #[inline]
27 const fn is_empty(&self) -> bool {
28 self.start >= self.end
29 }
30}
31
32pub struct BootAllocator {
33 regions: [BootRegion; MAX_BOOT_ALLOC_REGIONS],
34 len: usize,
35 accessible_limit: u64,
36}
37
38#[derive(Clone, Copy, Debug, Default)]
39pub struct BootAllocStats {
40 pub region_count: usize,
41 pub total_free_bytes: u64,
42 pub largest_region_bytes: u64,
43 pub accessible_limit: u64,
44}
45
46impl BootAllocator {
47 pub const fn new() -> Self {
48 Self {
49 regions: [BootRegion::empty(); MAX_BOOT_ALLOC_REGIONS],
50 len: 0,
51 accessible_limit: 0,
52 }
53 }
54
55 pub fn init(&mut self, regions: &[MemoryRegion]) {
56 {
59 static INIT_CALLS: core::sync::atomic::AtomicUsize =
60 core::sync::atomic::AtomicUsize::new(0);
61 let n = INIT_CALLS.fetch_add(1, core::sync::atomic::Ordering::Relaxed) + 1;
62 unsafe {
63 let hex = b"0123456789abcdef";
64 core::arch::asm!("out 0xe9, al", in("al") b'I', options(nomem, nostack));
65 for sh in [12usize, 8, 4, 0] {
66 let nib = hex[((n >> sh) & 0xF) as usize];
67 core::arch::asm!("out 0xe9, al", in("al") nib, options(nomem, nostack));
68 }
69 core::arch::asm!("out 0xe9, al", in("al") b'\n', options(nomem, nostack));
70 }
71 }
72 crate::e9_println!("BI init");
73 if self.len != 0 {
74 self.reset();
75 }
76 crate::e9_println!("BI reset");
77
78 for region in regions {
79 if !matches!(region.kind, MemoryKind::Free | MemoryKind::Reclaim) {
80 continue;
81 }
82
83 let start = align_up(region.base, PAGE_SIZE);
84 let end = align_down(region.base.saturating_add(region.size), PAGE_SIZE);
85 if start >= end {
86 continue;
87 }
88
89 self.push_region(BootRegion { start, end });
90 }
91 crate::e9_println!("BI push");
92
93 self.normalize_regions();
94 crate::e9_println!("BI norm1");
95
96 crate::e9_mark!(b'U');
97 let guard = PROTECTED_RANGES.lock();
98 crate::e9_mark!(b'u');
99 for entry in guard.iter() {
100 let (base, size) = match *entry {
101 Some(v) => v,
102 None => continue,
103 };
104 if size == 0 {
105 continue;
106 }
107 crate::e9_mark!(b'V');
108 self.exclude_range(
109 align_down(base, PAGE_SIZE),
110 align_up(base.saturating_add(size), PAGE_SIZE),
111 );
112 crate::e9_mark!(b'v');
113 }
114 drop(guard);
115 crate::e9_mark!(b'W');
116
117 self.normalize_regions();
118 crate::e9_println!("BI norm2 pre");
119 self.rebuild_accessible_limit();
120 crate::e9_println!("BI done");
121 }
122
123 pub fn alloc(&mut self, size: usize, align: usize) -> PhysAddr {
124 self.try_alloc(size, align).unwrap_or_else(|| {
125 panic!(
126 "boot allocator: out of physical memory for size={} align={}",
127 size, align
128 )
129 })
130 }
131
132 pub fn try_alloc(&mut self, size: usize, align: usize) -> Option<PhysAddr> {
133 if crate::debug_cfg::is_quiet() {
134 unsafe { core::arch::asm!("out 0xe9, al", in("al") b'b', options(nomem, nostack)) };
136 }
137 if size == 0 {
138 return Some(PhysAddr::new(0));
139 }
140
141 let align = normalize_align(align) as u64;
142 let size = align_up(size as u64, PAGE_SIZE);
143
144 for idx in 0..self.len {
145 let region = self.regions[idx];
146 if region.is_empty() {
147 continue;
148 }
149
150 let alloc_start = align_up(region.start, align);
151 let alloc_end = alloc_start.checked_add(size)?;
152 if alloc_end > region.end {
153 continue;
154 }
155
156 self.consume_region(idx, alloc_start, alloc_end);
157 return Some(PhysAddr::new(alloc_start));
158 }
159
160 let stats = self.stats();
161 serial_println!(
162 "[boot_alloc] try_alloc failed: requested={} aligned={} largest_region={} regions={} total_free={}",
163 size as usize,
164 align as usize,
165 stats.largest_region_bytes as usize,
166 stats.region_count,
167 stats.total_free_bytes as usize
168 );
169 None
170 }
171
172 pub fn try_alloc_accessible(&mut self, size: usize, align: usize) -> Option<PhysAddr> {
173 if crate::debug_cfg::is_quiet() {
174 unsafe { core::arch::asm!("out 0xe9, al", in("al") b'a', options(nomem, nostack)) };
175 }
176 if size == 0 {
177 return Some(PhysAddr::new(0));
178 }
179
180 let align = normalize_align(align) as u64;
181 let size = align_up(size as u64, PAGE_SIZE);
182
183 for idx in 0..self.len {
184 let region = self.regions[idx];
185 if region.is_empty() {
186 continue;
187 }
188
189 let alloc_start = align_up(region.start, align);
190 let alloc_end = alloc_start.checked_add(size)?;
191 if alloc_end > region.end {
192 continue;
193 }
194
195 if self.accessible_limit != 0 && alloc_end > self.accessible_limit {
196 continue;
197 }
198
199 if !crate::memory::paging::is_hhdm_range_mapped_now(alloc_start, size) {
200 continue;
201 }
202
203 self.consume_region(idx, alloc_start, alloc_end);
204 return Some(PhysAddr::new(alloc_start));
205 }
206
207 let stats = self.stats();
208 serial_println!(
209 "[boot_alloc] try_alloc_accessible failed: requested={} aligned={} largest_region={} regions={} total_free={} accessible_limit={:#x}",
210 size as usize,
211 align as usize,
212 stats.largest_region_bytes as usize,
213 stats.region_count,
214 stats.total_free_bytes as usize
215 ,stats.accessible_limit
216 );
217 None
218 }
219
220 pub fn snapshot_free_regions(&self, out: &mut [MemoryRegion]) -> usize {
221 let count = core::cmp::min(self.len, out.len());
222 for (dst, region) in out.iter_mut().zip(self.regions.iter()).take(count) {
223 *dst = MemoryRegion {
224 base: region.start,
225 size: region.end.saturating_sub(region.start),
226 kind: MemoryKind::Free,
227 };
228 }
229 count
230 }
231
232 fn reset(&mut self) {
233 self.regions = [BootRegion::empty(); MAX_BOOT_ALLOC_REGIONS];
234 self.len = 0;
235 self.accessible_limit = 0;
236 }
237
238 fn push_region(&mut self, region: BootRegion) {
239 if region.is_empty() || self.len >= self.regions.len() {
240 return;
241 }
242 self.regions[self.len] = region;
243 self.len += 1;
244 }
245
246 fn exclude_range(&mut self, exclude_start: u64, exclude_end: u64) {
247 if exclude_start >= exclude_end {
248 return;
249 }
250
251 let mut idx = 0usize;
252 while idx < self.len {
253 let region = self.regions[idx];
254 if exclude_end <= region.start || exclude_start >= region.end {
255 idx += 1;
256 continue;
257 }
258
259 if exclude_start <= region.start && exclude_end >= region.end {
260 self.remove_region(idx);
261 continue;
262 }
263
264 if exclude_start <= region.start {
265 self.regions[idx].start = exclude_end.min(region.end);
266 idx += 1;
267 continue;
268 }
269
270 if exclude_end >= region.end {
271 self.regions[idx].end = exclude_start.max(region.start);
272 idx += 1;
273 continue;
274 }
275
276 let right = BootRegion {
277 start: exclude_end,
278 end: region.end,
279 };
280 self.regions[idx].end = exclude_start;
281 if self.len < self.regions.len() {
282 self.insert_region(idx + 1, right);
283 }
284 idx += 2;
285 }
286
287 self.normalize_regions();
288 }
289
290 fn consume_region(&mut self, idx: usize, alloc_start: u64, alloc_end: u64) {
291 let region = self.regions[idx];
292
293 if alloc_start <= region.start && alloc_end >= region.end {
294 self.remove_region(idx);
295 self.rebuild_accessible_limit();
296 return;
297 }
298
299 if alloc_start <= region.start {
300 self.regions[idx].start = alloc_end;
301 self.rebuild_accessible_limit();
302 return;
303 }
304
305 if alloc_end >= region.end {
306 self.regions[idx].end = alloc_start;
307 self.rebuild_accessible_limit();
308 return;
309 }
310
311 let right = BootRegion {
312 start: alloc_end,
313 end: region.end,
314 };
315 self.regions[idx].end = alloc_start;
316 if self.len < self.regions.len() {
317 self.insert_region(idx + 1, right);
318 }
319
320 self.normalize_regions();
321 self.rebuild_accessible_limit();
322 }
323
324 fn insert_region(&mut self, idx: usize, region: BootRegion) {
325 if region.is_empty() || self.len >= self.regions.len() {
326 return;
327 }
328
329 for slot in (idx..self.len).rev() {
330 self.regions[slot + 1] = self.regions[slot];
331 }
332 self.regions[idx] = region;
333 self.len += 1;
334 }
335
336 fn remove_region(&mut self, idx: usize) {
337 if idx >= self.len {
338 return;
339 }
340 for slot in idx..self.len.saturating_sub(1) {
341 self.regions[slot] = self.regions[slot + 1];
342 }
343 if self.len != 0 {
344 self.len -= 1;
345 self.regions[self.len] = BootRegion::empty();
346 }
347 }
348
349 fn normalize_regions(&mut self) {
350 crate::e9_println!("NR begin");
351 if self.len <= 1 {
352 crate::e9_mark!(b'S');
353 self.rebuild_accessible_limit();
354 crate::e9_mark!(b's');
355 return;
356 }
357
358 crate::e9_mark!(b'B');
359 for i in 1..self.len {
360 let cur = self.regions[i];
361 let mut j = i;
362 while j > 0 && self.regions[j - 1].start > cur.start {
363 self.regions[j] = self.regions[j - 1];
364 j -= 1;
365 }
366 self.regions[j] = cur;
367 }
368 crate::e9_mark!(b'b');
369
370 let mut write = 0usize;
371 for read in 0..self.len {
372 let cur = self.regions[read];
373 if cur.is_empty() {
374 continue;
375 }
376 if write == 0 {
377 self.regions[write] = cur;
378 write += 1;
379 continue;
380 }
381 let prev = self.regions[write - 1];
382 if cur.start <= prev.end {
383 self.regions[write - 1].end = prev.end.max(cur.end);
384 } else {
385 self.regions[write] = cur;
386 write += 1;
387 }
388 }
389 crate::e9_mark!(b'c');
390
391 for slot in write..self.regions.len() {
392 self.regions[slot] = BootRegion::empty();
393 }
394 self.len = write;
395 self.rebuild_accessible_limit();
396 crate::e9_mark!(b'd');
397 }
398
399 fn rebuild_accessible_limit(&mut self) {
401 REBUILD_CALL_COUNT.fetch_add(1, core::sync::atomic::Ordering::Relaxed);
403 let n = REBUILD_CALL_COUNT.load(core::sync::atomic::Ordering::Relaxed);
404 if n % 65536 == 0 {
405 unsafe {
406 let hex = b"0123456789abcdef";
407 core::arch::asm!("out 0xe9, al", in("al") b'$', options(nomem, nostack));
408 for sh in [28usize, 24, 20, 16, 12, 8, 4, 0] {
409 let nib = hex[((n >> sh) & 0xF) as usize];
410 core::arch::asm!("out 0xe9, al", in("al") nib, options(nomem, nostack));
411 }
412 core::arch::asm!("out 0xe9, al", in("al") b'\n', options(nomem, nostack));
413 }
414 }
415 crate::e9_mark!(b'Q');
416 let mut limit = 0u64;
417 crate::e9_mark!(b'R');
418 for region in self.regions.iter().take(self.len).copied() {
419 crate::e9_mark!(b'S');
420 limit = limit.max(self.accessible_prefix_end(region));
421 crate::e9_mark!(b's');
422 }
423 crate::e9_mark!(b'T');
424 self.accessible_limit = limit;
425 crate::e9_mark!(b't');
426 }
427
428 fn accessible_prefix_end(&self, region: BootRegion) -> u64 {
430 if region.is_empty() {
431 return 0;
432 }
433
434 let total_pages = ((region.end - region.start) / PAGE_SIZE) as usize;
435 if total_pages == 0 {
436 return 0;
437 }
438
439 let mut low = 0usize;
440 let mut high = total_pages;
441 while low < high {
442 let mid = (low + high).div_ceil(2);
443 let size = mid as u64 * PAGE_SIZE;
444 if crate::memory::paging::is_hhdm_range_mapped_now(region.start, size) {
445 low = mid;
446 } else {
447 high = mid - 1;
448 }
449 }
450
451 region.start + low as u64 * PAGE_SIZE
452 }
453
454 pub fn stats(&self) -> BootAllocStats {
455 let mut total = 0u64;
456 let mut largest = 0u64;
457 for region in self.regions.iter().take(self.len) {
458 let size = region.end.saturating_sub(region.start);
459 total = total.saturating_add(size);
460 largest = largest.max(size);
461 }
462 BootAllocStats {
463 region_count: self.len,
464 total_free_bytes: total,
465 largest_region_bytes: largest,
466 accessible_limit: self.accessible_limit,
467 }
468 }
469}
470
471static BOOT_ALLOCATOR: SpinLock<BootAllocator> = SpinLock::new(BootAllocator::new());
472static REBUILD_CALL_COUNT: core::sync::atomic::AtomicUsize =
473 core::sync::atomic::AtomicUsize::new(0);
474static PROTECTED_RANGES: SpinLock<[Option<(u64, u64)>; MAX_PROTECTED_RANGES]> =
475 SpinLock::new([None; MAX_PROTECTED_RANGES]);
476
477pub fn init_boot_allocator(regions: &[MemoryRegion]) {
478 crate::e9_println!("IBA enter");
479 let mut guard = BOOT_ALLOCATOR.lock();
480 crate::e9_println!("IBA locked");
481 guard.init(regions);
482 crate::e9_println!("IBA init done");
483 drop(guard);
484}
485
486pub fn get_boot_allocator() -> &'static SpinLock<BootAllocator> {
487 &BOOT_ALLOCATOR
488}
489
490pub fn boot_allocator_stats() -> BootAllocStats {
491 BOOT_ALLOCATOR.lock().stats()
492}
493
494pub fn alloc_bytes(size: usize, align: usize) -> Option<PhysAddr> {
495 BOOT_ALLOCATOR.lock().try_alloc(size, align)
496}
497
498pub fn alloc_bytes_accessible(size: usize, align: usize) -> Option<PhysAddr> {
499 BOOT_ALLOCATOR.lock().try_alloc_accessible(size, align)
500}
501
502pub fn snapshot_free_regions(out: &mut [MemoryRegion]) -> usize {
503 BOOT_ALLOCATOR.lock().snapshot_free_regions(out)
504}
505
506pub fn seal() {
513 BOOT_ALLOCATOR.lock().reset();
514}
515
516pub fn alloc_stack(size: usize) -> Option<u64> {
517 let phys = alloc_bytes(size, PAGE_SIZE as usize)?;
518 let span = align_up(size as u64, PAGE_SIZE);
519 Some(phys_to_virt(phys.as_u64()).saturating_add(span))
520}
521
522pub fn set_protected_ranges(ranges: &[Option<(u64, u64)>]) {
523 let mut protected = PROTECTED_RANGES.lock();
524 *protected = [None; MAX_PROTECTED_RANGES];
525 for (dst, src) in protected.iter_mut().zip(ranges.iter().copied()) {
526 *dst = src;
527 }
528}
529
530pub fn reset_protected_ranges() {
531 *PROTECTED_RANGES.lock() = [None; MAX_PROTECTED_RANGES];
532}
533
534pub(crate) fn protected_ranges_snapshot() -> [Option<(u64, u64)>; MAX_PROTECTED_RANGES] {
535 crate::e9_mark!(b'p');
536 let guard = PROTECTED_RANGES.lock();
537 crate::e9_mark!(b'q');
538 let mut result = [None; MAX_PROTECTED_RANGES];
541 for (dst, src) in result.iter_mut().zip(guard.iter()) {
542 *dst = *src;
543 }
544 crate::e9_mark!(b'r');
545 result
546}
547
548#[inline]
549const fn normalize_align(align: usize) -> usize {
550 let align = if align == 0 { 1 } else { align };
551 let align = if align < PAGE_SIZE as usize {
552 PAGE_SIZE as usize
553 } else {
554 align
555 };
556 if align.is_power_of_two() {
557 align
558 } else {
559 align.next_power_of_two()
560 }
561}
562
563#[inline]
564const fn align_up(value: u64, align: u64) -> u64 {
565 (value + align - 1) & !(align - 1)
566}
567
568#[inline]
569const fn align_down(value: u64, align: u64) -> u64 {
570 value & !(align - 1)
571}