1use core::cmp::Ordering;
17use core::iter::Peekable;
18use core::mem::offset_of;
19use core::slice;
20
21use zr::static_assert;
22
23macro_rules! define_type_enum {
25 (
26 $(#[$enum_attr:meta])*
27 $vis:vis enum $name:ident {
28 $(
29 $(#[$variant_attr:meta])*
30 $variant:ident
31 ),* $(,)?
32 }
33 ) => {
34 paste::paste! {
35 $(#[$enum_attr])*
36 $vis enum $name {
37 $(
38 $(#[$variant_attr])*
39 $variant = bindings::memalloc_Type::[<k $variant>] as u64,
40 )*
41 }
42 }
43 };
44}
45
46define_type_enum! {
47 #[derive(Debug, Clone, Copy, PartialEq, Eq)]
69 #[repr(u64)]
70 pub enum Type {
71 FreeRam,
72 Peripheral,
73 Reserved,
74 PoolBookkeeping,
76 PhysKernel,
78 PhysElf,
80 PhysLog,
82 Kernel,
84 KernelStorage,
86 DataZbi,
88 TemporaryPhysHandoff,
90 PermanentPhysHandoff,
92 Vdso,
94 Userboot,
96 BootMachineStack,
98 BootShadowCallStack,
100 BootUnsafeStack,
102 FixedAddressStagingKernel,
105 FixedAddressStagingDataZbi,
108 LegacyBootData,
110 TemporaryIdentityPageTables,
113 KernelPageTables,
115 PhysDebugdata,
117 DevicetreeBlob,
119 PhysScratch,
122 PoolTestPayload,
124 ZbiTestPayload,
126 TestRamReserve,
128 Nvram,
130 ReservedLow,
134 TruncatedRam,
137 }
138}
139
140static_assert!(size_of::<Type>() == size_of::<bindings::memalloc_Type>());
142static_assert!(align_of::<Type>() == align_of::<bindings::memalloc_Type>());
143
144impl Type {
145 pub const MIN_ALLOCATED_VALUE: u64 = bindings::memalloc_kMinAllocatedTypeValue;
147
148 pub const fn as_str(&self) -> &'static str {
150 match self {
151 Type::FreeRam => "free RAM",
152 Type::Reserved => "reserved",
153 Type::Peripheral => "peripheral",
154 Type::PoolBookkeeping => "bookkeeping",
155 Type::PhysKernel => "phys ZBI kernel image",
156 Type::PhysElf => "phys ELF image",
157 Type::PhysLog => "phys log file",
158 Type::Kernel => "kernel image",
159 Type::KernelStorage => "decompressed kernel payload",
160 Type::DataZbi => "data ZBI",
161 Type::TemporaryPhysHandoff => "phys hand-off data (temporary)",
162 Type::PermanentPhysHandoff => "phys hand-off data (permanent)",
163 Type::Vdso => "vDSO",
164 Type::Userboot => "userboot",
165 Type::BootMachineStack => "boot machine stack",
166 Type::BootShadowCallStack => "boot shadow call stack",
167 Type::BootUnsafeStack => "boot unsafe stack",
168 Type::FixedAddressStagingKernel => "trampoline staging kernel image",
169 Type::FixedAddressStagingDataZbi => "trampoline staging data ZBI",
170 Type::LegacyBootData => "legacy boot data",
171 Type::TemporaryIdentityPageTables => "temporary identity page tables",
172 Type::KernelPageTables => "kernel page tables",
173 Type::PhysDebugdata => "phys debugdata",
174 Type::DevicetreeBlob => "devicetree blob",
175 Type::PhysScratch => "phys scratch",
176 Type::PoolTestPayload => "memalloc::Pool test payload",
177 Type::ZbiTestPayload => "ZBI test payload",
178 Type::TestRamReserve => "kernel.test.ram.reserve",
179 Type::Nvram => "ZBI_TYPE_NVRAM",
180 Type::ReservedLow => "reserved low memory",
181 Type::TruncatedRam => "truncated RAM",
182 }
183 }
184
185 pub const fn is_allocated(&self) -> bool {
187 (*self as u64) >= Self::MIN_ALLOCATED_VALUE
188 }
189
190 pub const fn is_ram(&self) -> bool {
192 matches!(*self, Type::FreeRam) || self.is_allocated()
193 }
194}
195
196#[repr(C)]
204#[derive(Debug, Clone, Copy, Eq, PartialEq)]
205pub struct Range {
206 addr: u64,
207 size: u64,
208 ty: Type,
209}
210
211static_assert!(size_of::<Range>() == size_of::<bindings::memalloc_Range>());
213static_assert!(align_of::<Range>() == align_of::<bindings::memalloc_Range>());
214static_assert!(offset_of!(Range, addr) == offset_of!(bindings::memalloc_Range, addr));
215static_assert!(offset_of!(Range, size) == offset_of!(bindings::memalloc_Range, size));
216static_assert!(offset_of!(Range, ty) == offset_of!(bindings::memalloc_Range, type_));
217
218static_assert!(size_of::<Range>() == size_of::<zbi::MemRange>());
220static_assert!(align_of::<Range>() == align_of::<zbi::MemRange>());
221static_assert!(offset_of!(Range, addr) == offset_of!(zbi::MemRange, paddr));
222static_assert!(offset_of!(Range, size) == offset_of!(zbi::MemRange, length));
223static_assert!(offset_of!(Range, ty) == offset_of!(zbi::MemRange, r#type));
224
225impl Range {
226 pub const fn new(addr: u64, size: u64, ty: Type) -> Self {
230 let end = addr.saturating_add(size);
231 Self { addr, size: end - addr, ty }
232 }
233
234 pub fn slice_from_zbi_mem_ranges(ranges: &mut [zbi::MemRange]) -> &mut [Self] {
236 for range in ranges.iter_mut() {
237 range.length = range.paddr.saturating_add(range.length) - range.paddr;
238 range.reserved = 0;
239 }
240 unsafe { slice::from_raw_parts_mut(ranges.as_mut_ptr().cast::<Range>(), ranges.len()) }
250 }
251
252 pub const fn addr(&self) -> u64 {
254 self.addr
255 }
256
257 pub const fn size(&self) -> u64 {
259 self.size
260 }
261
262 pub const fn ty(&self) -> Type {
264 self.ty
265 }
266
267 pub const fn end(&self) -> u64 {
269 self.addr + self.size
270 }
271
272 pub const fn align_out(&self, alignment: u64) -> Self {
281 assert!(alignment.is_power_of_two());
282 let aligned_addr = self.addr & alignment.wrapping_neg();
283 let aligned_end = match self.end().checked_add(alignment - 1) {
284 Some(end) => end & alignment.wrapping_neg(),
285 None => u64::MAX,
286 };
287 Self { addr: aligned_addr, size: aligned_end - aligned_addr, ty: self.ty }
288 }
289
290 pub const fn intersects(&self, other: &Range) -> bool {
294 self.addr < other.end() && other.addr < self.end()
295 }
296}
297
298impl PartialOrd for Range {
299 fn partial_cmp(&self, other: &Range) -> Option<Ordering> {
304 match (self.addr.cmp(&other.addr), self.size.cmp(&other.size)) {
305 (Ordering::Equal, Ordering::Equal) => {
306 if self.ty == other.ty {
307 Some(Ordering::Equal)
308 } else {
309 None
310 }
311 }
312 (Ordering::Equal, size_ord) => Some(size_ord),
313 (addr_ord, _) => Some(addr_ord),
314 }
315 }
316}
317
318pub unsafe trait NormalizedRangeIterator: Iterator<Item = Range> {
330 fn filter_map_ranges<F>(self, f: F) -> FilterMapRanges<Self, F>
335 where
336 Self: Sized,
337 F: Fn(Type) -> Option<Type>,
338 {
339 FilterMapRanges::new(self, f)
340 }
341
342 fn filter_map_ram(self) -> impl NormalizedRangeIterator
345 where
346 Self: Sized,
347 {
348 self.filter_map_ranges(|t| if t.is_ram() { Some(Type::FreeRam) } else { None })
349 }
350}
351
352#[derive(Clone)]
356pub struct FilterMapRanges<Ranges, F> {
357 ranges: Ranges,
358 f: F,
359 pending: Option<Range>,
360}
361
362impl<Ranges, F> FilterMapRanges<Ranges, F>
363where
364 Ranges: NormalizedRangeIterator,
365 F: Fn(Type) -> Option<Type>,
366{
367 fn new(ranges: impl IntoIterator<IntoIter = Ranges>, f: F) -> Self {
368 Self { ranges: ranges.into_iter(), f, pending: None }
369 }
370}
371
372impl<Ranges, F> Iterator for FilterMapRanges<Ranges, F>
373where
374 Ranges: NormalizedRangeIterator,
375 F: Fn(Type) -> Option<Type>,
376{
377 type Item = Range;
378
379 fn next(&mut self) -> Option<Range> {
380 let Some(cur) = self.ranges.next() else {
385 return self.pending.take();
387 };
388
389 let Some(ty) = (self.f)(cur.ty) else {
391 return self.next();
393 };
394
395 let cur = Range { addr: cur.addr, size: cur.size, ty };
396
397 let Some(prev) = self.pending.take() else {
398 self.pending = Some(cur);
400 return self.next();
401 };
402
403 if prev.end() == cur.addr && prev.ty == cur.ty {
404 self.pending = Some(Range::new(prev.addr, prev.size + cur.size, prev.ty));
405 return self.next();
406 }
407
408 self.pending = Some(cur);
409 Some(prev)
410 }
411}
412
413unsafe impl<I, F> NormalizedRangeIterator for FilterMapRanges<I, F>
415where
416 I: NormalizedRangeIterator,
417 F: Fn(Type) -> Option<Type>,
418{
419}
420
421unsafe impl<I> NormalizedRangeIterator for Peekable<I> where I: NormalizedRangeIterator {}
425
426#[cfg(test)]
427mod tests {
428 use super::super::*;
429 use super::*;
430
431 const TEST_RANGES: [Range; 6] = [
433 Range::new(0, 0x1000, Type::PoolBookkeeping),
434 Range::new(0x1000, 0x1000, Type::DataZbi),
435 Range::new(0x2000, 0x1000, Type::PoolTestPayload),
436 Range::new(0x3000, 0x1000, Type::Peripheral),
437 Range::new(0x5000, 0x1000, Type::PoolTestPayload),
438 Range::new(0x6000, 0x1000, Type::PhysKernel),
439 ];
440
441 #[test]
443 fn range_test_new() {
444 assert_eq!(
445 Range::new(0, 0x1000, Type::FreeRam),
446 Range { addr: 0, size: 0x1000, ty: Type::FreeRam },
447 );
448 }
449
450 #[test]
452 fn range_test_new_saturates() {
453 assert_eq!(
454 Range::new(u64::MAX - 1, 0x1000, Type::FreeRam),
455 Range::new(u64::MAX - 1, 1, Type::FreeRam),
456 );
457 assert_eq!(Range::new(u64::MAX, 1, Type::FreeRam), Range::new(u64::MAX, 0, Type::FreeRam));
458 }
459
460 #[test]
462 fn range_test_align_out_start_rounds_down() {
463 let r = Range::new(1, 0x1000 - 1, Type::FreeRam);
464 assert_eq!(r.align_out(0x1000), Range::new(0, 0x1000, Type::FreeRam));
465 }
466
467 #[test]
469 fn range_test_align_out_end_rounds_up() {
470 let r = Range::new(0, 0x1000 - 1, Type::FreeRam);
471 assert_eq!(r.align_out(0x1000), Range::new(0, 0x1000, Type::FreeRam));
472 }
473
474 #[test]
476 fn range_test_align_out_range_smaller_than_alignment() {
477 let r = Range::new(1, 1, Type::FreeRam);
478 assert_eq!(r.align_out(0x1000), Range::new(0, 0x1000, Type::FreeRam));
479 }
480
481 #[test]
483 fn range_test_align_out_both_ends_change() {
484 let r = Range::new(1, 0x1000, Type::FreeRam);
485 assert_eq!(r.align_out(0x1000), Range::new(0, 2 * 0x1000, Type::FreeRam));
486 }
487
488 #[test]
490 fn range_test_align_out_saturates() {
491 let r = Range::new(u64::MAX - 1, 1, Type::FreeRam);
492 assert_eq!(
493 r.align_out(0x1000),
494 Range::new(u64::MAX - 0x1000 + 1, 0x1000 - 1, Type::FreeRam),
495 );
496 }
497
498 #[test]
500 fn range_test_align_out_empty_range() {
501 let r = Range::new(0x3000 + 1, 0, Type::FreeRam);
502 assert_eq!(r.align_out(0x1000), Range::new(0x3000, 0x1000, Type::FreeRam));
503 }
504
505 #[test]
512 fn range_test_intersects_disjoint() {
513 let a = Range::new(0, 0x1000, Type::FreeRam);
514 let b = Range::new(0x1000, 0x1000, Type::FreeRam);
515 assert!(!a.intersects(&b));
516 assert!(!b.intersects(&a));
517 }
518
519 #[test]
526 fn range_test_intersects_identity() {
527 let r = Range::new(0, 0x1000, Type::FreeRam);
528 assert!(r.intersects(&r));
529 }
530
531 #[test]
538 fn range_test_intersects_eclipse() {
539 let outer = Range::new(0, 0x3000, Type::FreeRam);
540 let inner = Range::new(0x1000, 0x1000, Type::FreeRam);
541 assert!(outer.intersects(&inner));
542 assert!(inner.intersects(&outer));
543 }
544
545 #[test]
552 fn range_test_intersects_clip_right() {
553 let r = Range::new(0, 0x2000, Type::FreeRam);
554 let other = Range::new(0x1000, 0x2000, Type::FreeRam);
555 assert!(r.intersects(&other));
556 }
557
558 #[test]
565 fn range_test_intersects_clip_left() {
566 let r = Range::new(0x1000, 0x2000, Type::FreeRam);
567 let other = Range::new(0, 0x2000, Type::FreeRam);
568 assert!(r.intersects(&other));
569 }
570
571 #[test]
578 fn range_test_intersects_type_agnostic() {
579 let a = Range::new(0, 0x2000, Type::FreeRam);
580 let b = Range::new(0x1000, 0x2000, Type::Peripheral);
581 assert!(a.intersects(&b));
582 }
583
584 #[test]
591 fn range_test_intersects_empty_range_with_other_zero_sized_range() {
592 let a = Range::new(0x3000, 0, Type::FreeRam);
593 let b = Range::new(0x3000, 0, Type::Peripheral);
594 assert!(!a.intersects(&b));
595 }
596
597 #[test]
604 fn range_test_intersects_empty_range_at_same_start_address() {
605 let a = Range::new(0x1000, 0, Type::FreeRam);
606 let b = Range::new(0x1000, 0x2000, Type::Peripheral);
607 assert!(!a.intersects(&b));
608 }
609
610 #[test]
617 fn range_test_intersects_type_empty_range_eclipsed() {
618 let a = Range::new(0x3000, 0, Type::FreeRam);
619 let b = Range::new(0x1000, 0x10000, Type::Peripheral);
620 assert!(a.intersects(&b));
621 }
622
623 #[test]
630 fn range_test_partial_cmp_addr_less_than() {
631 let a = Range::new(0, 0x1000, Type::FreeRam);
632 let b = Range::new(0x1000, 0x1000, Type::Peripheral);
633 assert_eq!(a.partial_cmp(&b), Some(Ordering::Less));
634 assert_eq!(b.partial_cmp(&a), Some(Ordering::Greater));
635 }
636
637 #[test]
644 fn range_test_partial_cmp_smaller() {
645 let a = Range::new(0, 0x1000, Type::FreeRam);
646 let b = Range::new(0, 0x2000, Type::Peripheral);
647 assert_eq!(a.partial_cmp(&b), Some(Ordering::Less));
648 assert_eq!(b.partial_cmp(&a), Some(Ordering::Greater));
649 }
650
651 #[test]
660 fn range_test_partial_cmp_eq() {
661 let a = Range::new(0, 0x1000, Type::FreeRam);
662 let b = Range::new(0, 0x1000, Type::FreeRam);
663 let c = Range::new(0, 0x1000, Type::Peripheral);
664 assert_eq!(a.partial_cmp(&b), Some(Ordering::Equal));
665 assert_eq!(a.partial_cmp(&c), None);
666 }
667
668 #[test]
671 fn range_test_from_zbi_ranges() {
672 fn check_ranges(ranges: &[Range]) {
673 assert_eq!(
674 ranges,
675 &[
676 Range::new(0, 0x1000, Type::FreeRam),
677 Range::new(0x1000, 0x1000, Type::Peripheral),
678 Range::new(u64::MAX - 1, 1, Type::Reserved),
679 ]
680 );
681 }
682
683 let mut zbi_ranges = [
684 zbi::MemRange {
685 paddr: 0,
686 length: 0x1000,
687 r#type: zbi::MemType::Ram,
688 reserved: 0xdeadbeef,
689 },
690 zbi::MemRange {
691 paddr: 0x1000,
692 length: 0x1000,
693 r#type: zbi::MemType::Peripheral,
694 reserved: 0x12345678,
695 },
696 zbi::MemRange {
697 paddr: u64::MAX - 1,
698 length: 0x1000,
699 r#type: zbi::MemType::Reserved,
700 reserved: u32::MAX,
701 },
702 ];
703
704 check_ranges(Range::slice_from_zbi_mem_ranges(&mut zbi_ranges));
705 }
706
707 #[test]
712 fn normalized_range_iterator_test_filter_map_ram() {
713 let mut actual =
714 testing::NormalizedRangeCheckingIterator::new(TEST_RANGES).filter_map_ram();
715 assert_eq!(actual.next(), Some(Range::new(0, 0x3000, Type::FreeRam)));
716 assert_eq!(actual.next(), Some(Range::new(0x5000, 0x2000, Type::FreeRam)));
717 assert_eq!(actual.next(), None);
718 }
719
720 #[test]
725 fn normalized_range_iterator_test_filter_map_ranges_discard_all() {
726 let mut actual =
727 testing::NormalizedRangeCheckingIterator::new(TEST_RANGES).filter_map_ranges(|_| None);
728 assert_eq!(actual.next(), None);
729 }
730
731 #[test]
736 fn normalized_range_iterator_test_filter_map_ranges_discard_ram() {
737 let mut actual = testing::NormalizedRangeCheckingIterator::new(TEST_RANGES)
738 .filter_map_ranges(|r| if r.is_ram() { None } else { Some(r) });
739 assert_eq!(actual.next(), Some(Range::new(0x3000, 0x1000, Type::Peripheral)));
740 assert_eq!(actual.next(), None);
741 }
742
743 #[test]
748 fn normalized_range_iterator_test_filter_map_ranges_keep_pool_test_payloads_and_bookkeeping() {
749 let mut actual = testing::NormalizedRangeCheckingIterator::new(TEST_RANGES)
750 .filter_map_ranges(|r| {
751 if r == Type::PoolBookkeeping || r == Type::PoolTestPayload {
752 Some(r)
753 } else {
754 None
755 }
756 });
757 assert_eq!(actual.next(), Some(Range::new(0, 0x1000, Type::PoolBookkeeping)));
758 assert_eq!(actual.next(), Some(Range::new(0x2000, 0x1000, Type::PoolTestPayload)));
759 assert_eq!(actual.next(), Some(Range::new(0x5000, 0x1000, Type::PoolTestPayload)));
760 assert_eq!(actual.next(), None);
761 }
762}