1use crate::sealed::Sealed;
4use crate::structures::paging::page_table::PageTableLevel;
5use crate::structures::paging::PageTableIndex;
6use crate::VirtAddr;
7use core::convert::TryFrom;
8use core::fmt;
9#[cfg(feature = "step_trait")]
10use core::iter::Step;
11use core::marker::PhantomData;
12use core::ops::{Add, AddAssign, Sub, SubAssign};
13
14pub trait PageSize: Copy + Eq + PartialOrd + Ord + Sealed {
16 const SIZE: u64;
18
19 const DEBUG_STR: &'static str;
21}
22
23pub trait NotGiantPageSize: PageSize {}
25
26#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash)]
28pub enum Size4KiB {}
29
30#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash)]
32pub enum Size2MiB {}
33
34#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash)]
38pub enum Size1GiB {}
39
40impl PageSize for Size4KiB {
41 const SIZE: u64 = 4096;
42 const DEBUG_STR: &'static str = "4KiB";
43}
44
45impl NotGiantPageSize for Size4KiB {}
46
47impl Sealed for super::Size4KiB {}
48
49impl PageSize for Size2MiB {
50 const SIZE: u64 = Size4KiB::SIZE * 512;
51 const DEBUG_STR: &'static str = "2MiB";
52}
53
54impl NotGiantPageSize for Size2MiB {}
55
56impl Sealed for super::Size2MiB {}
57
58impl PageSize for Size1GiB {
59 const SIZE: u64 = Size2MiB::SIZE * 512;
60 const DEBUG_STR: &'static str = "1GiB";
61}
62
63impl Sealed for super::Size1GiB {}
64
65#[derive(Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash)]
67#[repr(C)]
68pub struct Page<S: PageSize = Size4KiB> {
69 start_address: VirtAddr,
70 size: PhantomData<S>,
71}
72
73impl<S: PageSize> Page<S> {
74 pub const SIZE: u64 = S::SIZE;
76
77 #[inline]
81 #[rustversion::attr(since(1.61), const)]
82 pub fn from_start_address(address: VirtAddr) -> Result<Self, AddressNotAligned> {
83 if !address.is_aligned_u64(S::SIZE) {
84 return Err(AddressNotAligned);
85 }
86 Ok(Page::containing_address(address))
87 }
88
89 #[inline]
95 #[rustversion::attr(since(1.61), const)]
96 pub unsafe fn from_start_address_unchecked(start_address: VirtAddr) -> Self {
97 Page {
98 start_address,
99 size: PhantomData,
100 }
101 }
102
103 #[inline]
105 #[rustversion::attr(since(1.61), const)]
106 pub fn containing_address(address: VirtAddr) -> Self {
107 Page {
108 start_address: address.align_down_u64(S::SIZE),
109 size: PhantomData,
110 }
111 }
112
113 #[inline]
115 #[rustversion::attr(since(1.61), const)]
116 pub fn start_address(self) -> VirtAddr {
117 self.start_address
118 }
119
120 #[inline]
122 #[rustversion::attr(since(1.61), const)]
123 pub fn size(self) -> u64 {
124 S::SIZE
125 }
126
127 #[inline]
129 #[rustversion::attr(since(1.61), const)]
130 pub fn p4_index(self) -> PageTableIndex {
131 self.start_address().p4_index()
132 }
133
134 #[inline]
136 #[rustversion::attr(since(1.61), const)]
137 pub fn p3_index(self) -> PageTableIndex {
138 self.start_address().p3_index()
139 }
140
141 #[inline]
143 #[rustversion::attr(since(1.61), const)]
144 pub fn page_table_index(self, level: PageTableLevel) -> PageTableIndex {
145 self.start_address().page_table_index(level)
146 }
147
148 #[inline]
150 #[rustversion::attr(since(1.61), const)]
151 pub fn range(start: Self, end: Self) -> PageRange<S> {
152 PageRange { start, end }
153 }
154
155 #[inline]
157 #[rustversion::attr(since(1.61), const)]
158 pub fn range_inclusive(start: Self, end: Self) -> PageRangeInclusive<S> {
159 PageRangeInclusive { start, end }
160 }
161
162 pub(crate) fn steps_between_u64(start: &Self, end: &Self) -> Option<u64> {
164 VirtAddr::steps_between_u64(&start.start_address(), &end.start_address())
165 .map(|steps| steps / S::SIZE)
166 }
167
168 #[cfg(any(feature = "instructions", feature = "step_trait"))]
170 pub(crate) fn steps_between_impl(start: &Self, end: &Self) -> (usize, Option<usize>) {
171 if let Some(steps) = Self::steps_between_u64(start, end) {
172 let steps = usize::try_from(steps).ok();
173 (steps.unwrap_or(usize::MAX), steps)
174 } else {
175 (0, None)
176 }
177 }
178
179 #[cfg(any(feature = "instructions", feature = "step_trait"))]
181 pub(crate) fn forward_checked_impl(start: Self, count: usize) -> Option<Self> {
182 let count = u64::try_from(count).ok()?.checked_mul(S::SIZE)?;
183 let start_address = VirtAddr::forward_checked_u64(start.start_address, count)?;
184 Some(Self {
185 start_address,
186 size: PhantomData,
187 })
188 }
189}
190
191impl<S: NotGiantPageSize> Page<S> {
192 #[inline]
194 #[rustversion::attr(since(1.61), const)]
195 pub fn p2_index(self) -> PageTableIndex {
196 self.start_address().p2_index()
197 }
198}
199
200impl Page<Size1GiB> {
201 #[inline]
203 #[rustversion::attr(since(1.61), const)]
204 pub fn from_page_table_indices_1gib(
205 p4_index: PageTableIndex,
206 p3_index: PageTableIndex,
207 ) -> Self {
208 let mut addr = 0;
209 addr |= p4_index.into_u64() << 39;
210 addr |= p3_index.into_u64() << 30;
211 Page::containing_address(VirtAddr::new_truncate(addr))
212 }
213}
214
215impl Page<Size2MiB> {
216 #[inline]
218 #[rustversion::attr(since(1.61), const)]
219 pub fn from_page_table_indices_2mib(
220 p4_index: PageTableIndex,
221 p3_index: PageTableIndex,
222 p2_index: PageTableIndex,
223 ) -> Self {
224 let mut addr = 0;
225 addr |= p4_index.into_u64() << 39;
226 addr |= p3_index.into_u64() << 30;
227 addr |= p2_index.into_u64() << 21;
228 Page::containing_address(VirtAddr::new_truncate(addr))
229 }
230}
231
232impl Page<Size4KiB> {
233 #[inline]
235 #[rustversion::attr(since(1.61), const)]
236 pub fn from_page_table_indices(
237 p4_index: PageTableIndex,
238 p3_index: PageTableIndex,
239 p2_index: PageTableIndex,
240 p1_index: PageTableIndex,
241 ) -> Self {
242 let mut addr = 0;
243 addr |= p4_index.into_u64() << 39;
244 addr |= p3_index.into_u64() << 30;
245 addr |= p2_index.into_u64() << 21;
246 addr |= p1_index.into_u64() << 12;
247 Page::containing_address(VirtAddr::new_truncate(addr))
248 }
249
250 #[inline]
252 pub const fn p1_index(self) -> PageTableIndex {
253 self.start_address.p1_index()
254 }
255}
256
257impl<S: PageSize> fmt::Debug for Page<S> {
258 fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
259 f.write_fmt(format_args!(
260 "Page[{}]({:#x})",
261 S::DEBUG_STR,
262 self.start_address().as_u64()
263 ))
264 }
265}
266
267impl<S: PageSize> Add<u64> for Page<S> {
268 type Output = Self;
269 #[inline]
270 fn add(self, rhs: u64) -> Self::Output {
271 Page::containing_address(self.start_address() + rhs * S::SIZE)
272 }
273}
274
275impl<S: PageSize> AddAssign<u64> for Page<S> {
276 #[inline]
277 fn add_assign(&mut self, rhs: u64) {
278 *self = *self + rhs;
279 }
280}
281
282impl<S: PageSize> Sub<u64> for Page<S> {
283 type Output = Self;
284 #[inline]
285 fn sub(self, rhs: u64) -> Self::Output {
286 Page::containing_address(self.start_address() - rhs * S::SIZE)
287 }
288}
289
290impl<S: PageSize> SubAssign<u64> for Page<S> {
291 #[inline]
292 fn sub_assign(&mut self, rhs: u64) {
293 *self = *self - rhs;
294 }
295}
296
297impl<S: PageSize> Sub<Self> for Page<S> {
298 type Output = u64;
299 #[inline]
300 fn sub(self, rhs: Self) -> Self::Output {
301 (self.start_address - rhs.start_address) / S::SIZE
302 }
303}
304
305#[cfg(feature = "step_trait")]
306impl<S: PageSize> Step for Page<S> {
307 fn steps_between(start: &Self, end: &Self) -> (usize, Option<usize>) {
308 Self::steps_between_impl(start, end)
309 }
310
311 fn forward_checked(start: Self, count: usize) -> Option<Self> {
312 Self::forward_checked_impl(start, count)
313 }
314
315 fn backward_checked(start: Self, count: usize) -> Option<Self> {
316 use core::convert::TryFrom;
317
318 let count = u64::try_from(count).ok()?.checked_mul(S::SIZE)?;
319 let start_address = VirtAddr::backward_checked_u64(start.start_address, count)?;
320 Some(Self {
321 start_address,
322 size: PhantomData,
323 })
324 }
325
326 #[cfg(not(kani))]
330 fn forward_overflowing(start: Self, count: usize) -> (Self, bool) {
331 match Self::forward_checked(start, count) {
332 Some(next) => (next, false),
333 None => (start, true),
334 }
335 }
336
337 #[cfg(not(kani))]
341 fn backward_overflowing(start: Self, count: usize) -> (Self, bool) {
342 match Self::backward_checked(start, count) {
343 Some(next) => (next, false),
344 None => (start, true),
345 }
346 }
347}
348
349#[derive(Clone, Copy, PartialEq, Eq, Hash)]
351#[repr(C)]
352pub struct PageRange<S: PageSize = Size4KiB> {
353 pub start: Page<S>,
355 pub end: Page<S>,
357}
358
359impl<S: PageSize> PageRange<S> {
360 #[inline]
362 pub fn is_empty(&self) -> bool {
363 self.start >= self.end
364 }
365
366 #[inline]
368 pub fn len(&self) -> u64 {
369 if !self.is_empty() {
370 self.end - self.start
371 } else {
372 0
373 }
374 }
375
376 #[inline]
378 pub fn size(&self) -> u64 {
379 S::SIZE * self.len()
380 }
381}
382
383impl<S: PageSize> Iterator for PageRange<S> {
384 type Item = Page<S>;
385
386 #[inline]
387 fn next(&mut self) -> Option<Self::Item> {
388 if self.start < self.end {
389 let page = self.start;
390 self.start += 1;
391 Some(page)
392 } else {
393 None
394 }
395 }
396
397 fn nth(&mut self, n: usize) -> Option<Self::Item> {
398 if self.is_empty() {
399 return None;
400 }
401
402 let n = u64::try_from(n).unwrap_or(u64::MAX);
406
407 if n >= self.len() {
411 self.nth(self.len() as usize - 1)?;
412 return self.next();
413 }
414
415 let second_half_start = Page::<S>::containing_address(VirtAddr::new(0xffff_8000_0000_0000));
417 let steps_until_gap = Page::steps_between_u64(&self.start, &second_half_start)
418 .filter(|steps| *steps <= n && *steps > 0);
419 if let Some(steps_until_gap) = steps_until_gap {
420 self.start += steps_until_gap - 1;
422 self.next()?;
424 unreachable!("the previous call to `next` should have panicked")
425 }
426
427 self.start += n;
428 self.next()
429 }
430
431 fn size_hint(&self) -> (usize, Option<usize>) {
432 let len = self.len();
433 usize::try_from(len)
434 .map(|len| (len, Some(len)))
435 .unwrap_or((usize::MAX, None))
436 }
437}
438
439impl<S: PageSize> DoubleEndedIterator for PageRange<S> {
440 #[inline]
441 fn next_back(&mut self) -> Option<Self::Item> {
442 if self.start < self.end {
443 self.end -= 1;
444 Some(self.end)
445 } else {
446 None
447 }
448 }
449
450 fn nth_back(&mut self, n: usize) -> Option<Self::Item> {
451 if self.is_empty() {
452 return None;
453 }
454
455 let n = u64::try_from(n).unwrap_or(u64::MAX);
459
460 if n >= self.len() {
464 self.nth_back(self.len() as usize - 1);
465 return self.next_back();
466 }
467
468 let first_half_end = Page::<S>::containing_address(VirtAddr::new(0x7fff_ffff_f000));
470 let steps_until_gap = Page::steps_between_u64(&first_half_end, &self.end)
471 .filter(|steps| *steps <= n && *steps > 0);
472 if let Some(steps_until_gap) = steps_until_gap {
473 self.end -= steps_until_gap - 1;
475 self.next_back()?;
477 unreachable!("the previous call to `next_back` should have panicked")
478 }
479
480 self.end -= n;
481 self.next_back()
482 }
483}
484
485impl PageRange<Size2MiB> {
486 #[inline]
488 pub fn as_4kib_page_range(self) -> PageRange<Size4KiB> {
489 PageRange {
490 start: Page::containing_address(self.start.start_address()),
491 end: Page::containing_address(self.end.start_address()),
492 }
493 }
494}
495
496impl<S: PageSize> fmt::Debug for PageRange<S> {
497 fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
498 f.debug_struct("PageRange")
499 .field("start", &self.start)
500 .field("end", &self.end)
501 .finish()
502 }
503}
504
505#[derive(Clone, Copy, PartialEq, Eq, Hash)]
507#[repr(C)]
508pub struct PageRangeInclusive<S: PageSize = Size4KiB> {
509 pub start: Page<S>,
511 pub end: Page<S>,
513}
514
515impl<S: PageSize> PageRangeInclusive<S> {
516 #[inline]
518 pub fn is_empty(&self) -> bool {
519 self.start > self.end
520 }
521
522 #[inline]
524 pub fn len(&self) -> u64 {
525 if !self.is_empty() {
526 self.end - self.start + 1
527 } else {
528 0
529 }
530 }
531
532 #[inline]
534 pub fn size(&self) -> u64 {
535 S::SIZE * self.len()
536 }
537}
538
539impl<S: PageSize> Iterator for PageRangeInclusive<S> {
540 type Item = Page<S>;
541
542 #[inline]
543 fn next(&mut self) -> Option<Self::Item> {
544 if self.start <= self.end {
545 let page = self.start;
546
547 let max_page_addr = VirtAddr::new(u64::MAX) - (S::SIZE - 1);
551 if self.start.start_address() < max_page_addr {
552 self.start += 1;
553 } else {
554 self.end -= 1;
555 }
556 Some(page)
557 } else {
558 None
559 }
560 }
561
562 fn nth(&mut self, n: usize) -> Option<Self::Item> {
563 if self.is_empty() {
564 return None;
565 }
566
567 let n = u64::try_from(n).unwrap_or(u64::MAX);
571
572 if n >= self.len() {
576 self.nth(self.len() as usize - 1)?;
577 return self.next();
578 }
579
580 let second_half_start = Page::<S>::containing_address(VirtAddr::new(0xffff_8000_0000_0000));
582 let steps_until_gap = Page::steps_between_u64(&self.start, &second_half_start)
583 .filter(|steps| *steps <= n && *steps > 0);
584 if let Some(steps_until_gap) = steps_until_gap {
585 self.start += steps_until_gap - 1;
587 self.next()?;
589 unreachable!("the previous call to `next` should have panicked")
590 }
591
592 self.start += n;
593 self.next()
594 }
595
596 fn size_hint(&self) -> (usize, Option<usize>) {
597 let len = self.len();
598 usize::try_from(len)
599 .map(|len| (len, Some(len)))
600 .unwrap_or((usize::MAX, None))
601 }
602}
603
604impl<S: PageSize> DoubleEndedIterator for PageRangeInclusive<S> {
605 #[inline]
606 fn next_back(&mut self) -> Option<Self::Item> {
607 if self.start <= self.end {
608 let page = self.end;
609
610 if self.end.start_address().as_u64() != 0 {
614 self.end -= 1;
615 } else {
616 self.start += 1;
617 }
618 Some(page)
619 } else {
620 None
621 }
622 }
623
624 fn nth_back(&mut self, n: usize) -> Option<Self::Item> {
625 if self.is_empty() {
626 return None;
627 }
628
629 let n = u64::try_from(n).unwrap_or(u64::MAX);
633
634 if n >= self.len() {
638 self.nth_back(self.len() as usize - 1);
639 return self.next_back();
640 }
641
642 let first_half_end = Page::<S>::containing_address(VirtAddr::new(0x7fff_ffff_f000));
644 let steps_until_gap = Page::steps_between_u64(&first_half_end, &self.end)
645 .filter(|steps| *steps <= n && *steps > 0);
646 if let Some(steps_until_gap) = steps_until_gap {
647 self.end -= steps_until_gap - 1;
649 self.next_back()?;
651 unreachable!("the previous call to `next_back` should have panicked")
652 }
653
654 self.end -= n;
655 self.next_back()
656 }
657}
658
659impl<S: PageSize> fmt::Debug for PageRangeInclusive<S> {
660 fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
661 f.debug_struct("PageRangeInclusive")
662 .field("start", &self.start)
663 .field("end", &self.end)
664 .finish()
665 }
666}
667
668#[cfg(kani)]
669impl<S: PageSize> kani::Arbitrary for Page<S> {
670 fn any() -> Self {
671 Self::containing_address(kani::any())
672 }
673}
674
675#[derive(Debug)]
677pub struct AddressNotAligned;
678
679impl fmt::Display for AddressNotAligned {
680 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
681 write!(f, "the given address was not sufficiently aligned")
682 }
683}
684
685#[cfg(test)]
686mod tests {
687 use super::*;
688
689 fn test_is_hash<T: core::hash::Hash>() {}
690
691 #[test]
692 pub fn test_page_is_hash() {
693 test_is_hash::<Page<Size4KiB>>();
694 test_is_hash::<Page<Size2MiB>>();
695 test_is_hash::<Page<Size1GiB>>();
696 }
697
698 #[test]
699 pub fn test_page_ranges() {
700 let page_size = Size4KiB::SIZE;
701 let number = 1000;
702
703 let start_addr = VirtAddr::new(0xdead_beaf);
704 let start: Page = Page::containing_address(start_addr);
705 let end = start + number;
706
707 let mut range = Page::range(start, end);
708 for i in 0..number {
709 assert_eq!(
710 range.next(),
711 Some(Page::containing_address(start_addr + page_size * i))
712 );
713 }
714 assert_eq!(range.next(), None);
715
716 let mut range_inclusive = Page::range_inclusive(start, end);
717 for i in 0..=number {
718 assert_eq!(
719 range_inclusive.next(),
720 Some(Page::containing_address(start_addr + page_size * i))
721 );
722 }
723 assert_eq!(range_inclusive.next(), None);
724 }
725
726 #[test]
727 pub fn test_page_range_inclusive_overflow() {
728 let page_size = Size4KiB::SIZE;
729 let number = 1000;
730
731 let start_addr = VirtAddr::new(u64::MAX).align_down(page_size) - number * page_size;
732 let start: Page = Page::containing_address(start_addr);
733 let end = start + number;
734
735 let mut range_inclusive = Page::range_inclusive(start, end);
736 for i in 0..=number {
737 assert_eq!(
738 range_inclusive.next(),
739 Some(Page::containing_address(start_addr + page_size * i))
740 );
741 }
742 assert_eq!(range_inclusive.next(), None);
743 }
744
745 #[test]
746 #[should_panic = "attempt to add resulted in non-canonical virtual address: VirtAddrNotValid(0x800000000000)"]
747 fn test_page_range_next_jumping_gap_panics() {
748 let start = 0x7fff_ffff_f000;
749 let end = 0xffff_8000_0000_0000;
750 let start = VirtAddr::new(start);
751 let end = VirtAddr::new(end);
752 let start = Page::<Size4KiB>::from_start_address(start).unwrap();
753 let end = Page::from_start_address(end).unwrap();
754 Page::range(start, end).next();
755 }
756
757 #[test]
759 #[should_panic = "attempt to subtract resulted in non-canonical virtual address: VirtAddrNotValid(0xffff7ffffffff000)"]
760 fn test_page_range_next_back_jumping_gap_panics() {
761 let start = 0x7fff_ffff_f000;
762 let end = 0xffff_8000_0000_0000;
763 let start = VirtAddr::new(start);
764 let end = VirtAddr::new(end);
765 let start = Page::<Size4KiB>::from_start_address(start).unwrap();
766 let end = Page::from_start_address(end).unwrap();
767 Page::range(start, end).next_back();
768 }
769
770 #[test]
771 #[should_panic = "attempt to add resulted in non-canonical virtual address: VirtAddrNotValid(0x800000000000)"]
772 fn test_page_range_inclusive_next_not_jumping_gap_panics() {
773 let start = 0x7fff_ffff_f000;
774 let end = 0x7fff_ffff_f000;
775 let start = VirtAddr::new(start);
776 let end = VirtAddr::new(end);
777 let start = Page::<Size4KiB>::from_start_address(start).unwrap();
778 let end = Page::from_start_address(end).unwrap();
779 Page::range_inclusive(start, end).next();
780 }
781
782 #[test]
783 #[should_panic = "attempt to subtract resulted in non-canonical virtual address: VirtAddrNotValid(0xffff7ffffffff000)"]
784 fn test_page_range_inclusive_next_back_not_jumping_gap_panics() {
785 let start = 0x7fff_ffff_f000;
786 let end = 0xffff_8000_0000_0000;
787 let start = VirtAddr::new(start);
788 let end = VirtAddr::new(end);
789 let start = Page::<Size4KiB>::from_start_address(start).unwrap();
790 let end = Page::from_start_address(end).unwrap();
791 Page::range_inclusive(start, end).next_back();
792 }
793
794 #[test]
796 #[should_panic = "attempt to add resulted in non-canonical virtual address: VirtAddrNotValid(0x800000000000)"]
797 fn test_page_range_inclusive_next_jumping_gap_panics() {
798 let start = 0x7fff_ffff_f000;
799 let end = 0x7fff_ffff_f000;
800 let start = VirtAddr::new(start);
801 let end = VirtAddr::new(end);
802 let start = Page::<Size4KiB>::from_start_address(start).unwrap();
803 let end = Page::from_start_address(end).unwrap();
804 Page::range_inclusive(start, end).next();
805 Page::range_inclusive(start, end).next();
806 }
807
808 #[test]
809 #[should_panic = "attempt to subtract resulted in non-canonical virtual address: VirtAddrNotValid(0xffff7ffffffff000)"]
810 fn test_page_range_inclusive_next_back_jumping_gap_panics() {
811 let start = 0xffff_8000_0000_0000;
812 let end = 0xffff_8000_0000_0000;
813 let start = VirtAddr::new(start);
814 let end = VirtAddr::new(end);
815 let start = Page::<Size4KiB>::from_start_address(start).unwrap();
816 let end = Page::from_start_address(end).unwrap();
817 Page::range_inclusive(start, end).next_back();
818 Page::range_inclusive(start, end).next_back();
819 }
820
821 #[test]
822 pub fn test_page_range_len() {
823 let start_addr = VirtAddr::new(0xdead_beaf);
824 let start = Page::<Size4KiB>::containing_address(start_addr);
825 let end = start + 50;
826
827 let range = PageRange { start, end };
828 assert_eq!(range.len(), 50);
829
830 let range_inclusive = PageRangeInclusive { start, end };
831 assert_eq!(range_inclusive.len(), 51);
832 }
833
834 #[test]
835 #[cfg(feature = "step_trait")]
836 fn page_step_forward() {
837 let test_cases = [
838 (0, 0, Some(0)),
839 (0, 1, Some(0x1000)),
840 (0x1000, 1, Some(0x2000)),
841 (0x7fff_ffff_f000, 1, Some(0xffff_8000_0000_0000)),
842 (0xffff_8000_0000_0000, 1, Some(0xffff_8000_0000_1000)),
843 (0xffff_ffff_ffff_f000, 1, None),
844 #[cfg(target_pointer_width = "64")]
845 (0x7fff_ffff_f000, 0x1_2345_6789, Some(0xffff_9234_5678_8000)),
846 #[cfg(target_pointer_width = "64")]
847 (0x7fff_ffff_f000, 0x8_0000_0000, Some(0xffff_ffff_ffff_f000)),
848 #[cfg(target_pointer_width = "64")]
849 (0x7fff_fff0_0000, 0x8_0000_00ff, Some(0xffff_ffff_ffff_f000)),
850 #[cfg(target_pointer_width = "64")]
851 (0x7fff_fff0_0000, 0x8_0000_0100, None),
852 #[cfg(target_pointer_width = "64")]
853 (0x7fff_ffff_f000, 0x8_0000_0001, None),
854 (0, 0x10_0000, Some(0x1_0000_0000)),
857 ];
858 for (start, count, result) in test_cases {
859 let start = Page::<Size4KiB>::from_start_address(VirtAddr::new(start)).unwrap();
860 let result = result
861 .map(|result| Page::<Size4KiB>::from_start_address(VirtAddr::new(result)).unwrap());
862 assert_eq!(Step::forward_checked(start, count), result);
863 }
864 }
865
866 #[test]
867 #[cfg(feature = "step_trait")]
868 fn page_step_backwards() {
869 let test_cases = [
870 (0, 0, Some(0)),
871 (0, 1, None),
872 (0x1000, 1, Some(0)),
873 (0xffff_8000_0000_0000, 1, Some(0x7fff_ffff_f000)),
874 (0xffff_8000_0000_1000, 1, Some(0xffff_8000_0000_0000)),
875 #[cfg(target_pointer_width = "64")]
876 (0xffff_9234_5678_8000, 0x1_2345_6789, Some(0x7fff_ffff_f000)),
877 #[cfg(target_pointer_width = "64")]
878 (0xffff_8000_0000_0000, 0x8_0000_0000, Some(0)),
879 #[cfg(target_pointer_width = "64")]
880 (0xffff_8000_0000_0000, 0x7_ffff_ff01, Some(0xff000)),
881 #[cfg(target_pointer_width = "64")]
882 (0xffff_8000_0000_0000, 0x8_0000_0001, None),
883 (0x1_0000_0000, 0x10_0000, Some(0)),
886 ];
887 for (start, count, result) in test_cases {
888 let start = Page::<Size4KiB>::from_start_address(VirtAddr::new(start)).unwrap();
889 let result = result
890 .map(|result| Page::<Size4KiB>::from_start_address(VirtAddr::new(result)).unwrap());
891 assert_eq!(Step::backward_checked(start, count), result);
892 }
893 }
894
895 #[test]
896 #[cfg(feature = "step_trait")]
897 fn page_steps_between() {
898 let test_cases = [
899 (0, 0, 0, Some(0)),
900 (0, 0x1000, 1, Some(1)),
901 (0x1000, 0, 0, None),
902 (0x1000, 0x1000, 0, Some(0)),
903 (0x7fff_ffff_f000, 0xffff_8000_0000_0000, 1, Some(1)),
904 (0xffff_8000_0000_0000, 0x7fff_ffff_f000, 0, None),
905 (0xffff_8000_0000_0000, 0xffff_8000_0000_0000, 0, Some(0)),
906 (0xffff_8000_0000_0000, 0xffff_8000_0000_1000, 1, Some(1)),
907 (0xffff_8000_0000_1000, 0xffff_8000_0000_0000, 0, None),
908 (0xffff_8000_0000_1000, 0xffff_8000_0000_1000, 0, Some(0)),
909 (
912 0x0000_0000_0000,
913 0x0001_0000_0000,
914 0x10_0000,
915 Some(0x10_0000),
916 ),
917 #[cfg(target_pointer_width = "64")]
924 (
925 0x0000_0000_0000,
926 0x1000_0000_0000,
927 0x1_0000_0000,
928 Some(0x1_0000_0000),
929 ),
930 #[cfg(not(target_pointer_width = "64"))]
931 (0x0000_0000_0000, 0x1000_0000_0000, usize::MAX, None),
932 ];
933 for (start, end, lower, upper) in test_cases {
934 let start = Page::<Size4KiB>::from_start_address(VirtAddr::new(start)).unwrap();
935 let end = Page::from_start_address(VirtAddr::new(end)).unwrap();
936 assert_eq!(Step::steps_between(&start, &end), (lower, upper));
937 }
938 }
939
940 #[test]
941 #[cfg(feature = "step_trait")]
942 fn page_step_overflowing() {
943 let page = |addr| Page::<Size4KiB>::from_start_address(VirtAddr::new(addr)).unwrap();
944
945 assert_eq!(
946 Step::forward_overflowing(page(0x7fff_ffff_f000), 1),
947 (page(0xffff_8000_0000_0000), false)
948 );
949 assert_eq!(
950 Step::backward_overflowing(page(0xffff_8000_0000_0000), 1),
951 (page(0x7fff_ffff_f000), false)
952 );
953
954 assert!(Step::forward_overflowing(page(0xffff_ffff_ffff_f000), 1).1);
955 assert!(Step::backward_overflowing(page(0), 1).1);
956 }
957}
958
959#[cfg(kani)]
960mod proofs {
961 use super::*;
962
963 fn page_range_next_harness(should_panic_mode: bool) {
964 let start = kani::any::<Page<Size4KiB>>();
965 let end = kani::any::<Page<Size4KiB>>();
966
967 let should_panic = start.start_address().as_u64() != 0x7fff_ffff_e000
970 || start.start_address().as_u64() != 0x7fff_ffff_f000;
971 kani::assume(should_panic == should_panic_mode);
972
973 if should_panic {
974 let mut our_range = Page::range(start, end);
976 our_range.next();
977 our_range.next();
978 return;
979 }
980
981 let mut our_range = Page::range(start, end);
983 let mut native_range = start..end;
984 assert_eq!(our_range.next(), native_range.next());
986 assert_eq!(our_range.next(), native_range.next());
988 }
989
990 #[kani::proof]
991 fn page_range_next() {
992 page_range_next_harness(false);
993 }
994
995 #[kani::proof]
996 #[kani::should_panic]
997 fn page_range_next_panic() {
998 page_range_next_harness(true);
999 }
1000
1001 fn page_range_next_back_harness(should_panic_mode: bool) {
1002 let start = kani::any::<Page<Size4KiB>>();
1003 let end = kani::any::<Page<Size4KiB>>();
1004
1005 let should_panic = start.start_address().as_u64() != 0xffff_8000_0000_0000
1008 || start.start_address().as_u64() != 0xffff_8000_0000_1000;
1009 kani::assume(should_panic == should_panic_mode);
1010
1011 if should_panic {
1012 let mut our_range = Page::range(start, end);
1014 our_range.next_back();
1015 our_range.next_back();
1016 return;
1017 }
1018
1019 let mut our_range = Page::range(start, end);
1021 let mut native_range = start..end;
1022 assert_eq!(our_range.next_back(), native_range.next_back());
1024 assert_eq!(our_range.next_back(), native_range.next_back());
1026 }
1027
1028 #[kani::proof]
1029 fn page_range_next_back() {
1030 page_range_next_back_harness(false);
1031 }
1032
1033 #[kani::proof]
1034 #[kani::should_panic]
1035 fn page_range_next_back_panic() {
1036 page_range_next_back_harness(true);
1037 }
1038
1039 fn page_range_inclusive_next_harness(should_panic_mode: bool) {
1040 let start = kani::any::<Page<Size4KiB>>();
1041 let end = kani::any::<Page<Size4KiB>>();
1042
1043 let should_panic = start.start_address().as_u64() != 0x7fff_ffff_e000
1046 || start.start_address().as_u64() != 0x7fff_ffff_f000;
1047 kani::assume(should_panic == should_panic_mode);
1048
1049 if should_panic {
1050 let mut our_range = Page::range_inclusive(start, end);
1052 our_range.next();
1053 our_range.next();
1054 return;
1055 }
1056
1057 let mut our_range = Page::range_inclusive(start, end);
1059 let mut native_range = start..=end;
1060 assert_eq!(our_range.next(), native_range.next());
1062 assert_eq!(our_range.next(), native_range.next());
1064 }
1065
1066 #[kani::proof]
1067 fn page_range_inclusive_next() {
1068 page_range_inclusive_next_harness(false);
1069 }
1070
1071 #[kani::proof]
1072 #[kani::should_panic]
1073 fn page_range_inclusive_next_panic() {
1074 page_range_inclusive_next_harness(true);
1075 }
1076
1077 fn page_range_inclusive_next_back_harness(should_panic_mode: bool) {
1078 let start = kani::any::<Page<Size4KiB>>();
1079 let end = kani::any::<Page<Size4KiB>>();
1080
1081 let should_panic = start.start_address().as_u64() != 0xffff_8000_0000_0000
1084 || start.start_address().as_u64() != 0xffff_8000_0000_1000;
1085 kani::assume(should_panic == should_panic_mode);
1086
1087 if should_panic {
1088 let mut our_range = Page::range_inclusive(start, end);
1090 our_range.next_back();
1091 our_range.next_back();
1092 return;
1093 }
1094
1095 let mut our_range = Page::range_inclusive(start, end);
1097 let mut native_range = start..=end;
1098 assert_eq!(our_range.next_back(), native_range.next_back());
1100 assert_eq!(our_range.next_back(), native_range.next_back());
1102 }
1103
1104 #[kani::proof]
1105 fn page_range_inclusive_next_back() {
1106 page_range_inclusive_next_back_harness(false);
1107 }
1108
1109 #[kani::proof]
1110 #[kani::should_panic]
1111 fn page_range_inclusive_next_back_panic() {
1112 page_range_inclusive_next_back_harness(true);
1113 }
1114
1115 fn page_range_nth_harness(should_panic_mode: bool) {
1116 let start = kani::any::<Page>();
1117 let end = kani::any::<Page>();
1118 let m = kani::any::<u64>();
1119 let n = kani::any::<u64>();
1120
1121 let offset = m
1124 .checked_add(n)
1125 .and_then(|sum| sum.checked_add(2))
1126 .and_then(|sum| sum.checked_mul(Size4KiB::SIZE));
1127 let expected_end =
1128 offset.and_then(|offset| start.start_address().as_u64().checked_add(offset));
1129 let should_panic = expected_end.is_some_and(|expected_end| {
1130 start.start_address().as_u64() <= 0x7fff_ffff_f000 && expected_end > 0x7fff_ffff_f000
1131 }) || expected_end.is_none();
1132 kani::assume(should_panic == should_panic_mode);
1133
1134 if should_panic {
1135 let mut our_range = Page::range(start, end);
1137 our_range.nth(n as usize);
1138 our_range.nth(m as usize);
1139 return;
1140 }
1141
1142 let mut our_range = Page::range(start, end);
1144 let mut native_range = start..end;
1145 assert_eq!(our_range.nth(m as usize), native_range.nth(m as usize));
1146 assert_eq!(our_range.nth(n as usize), native_range.nth(n as usize));
1147 }
1148
1149 #[kani::proof]
1150 #[kani::unwind(1)]
1151 fn page_range_nth() {
1152 page_range_nth_harness(false);
1153 }
1154
1155 #[kani::proof]
1156 #[kani::unwind(1)]
1157 #[kani::should_panic]
1158 fn page_range_nth_panic() {
1159 page_range_nth_harness(true);
1160 }
1161
1162 fn page_range_nth_back_harness(should_panic_mode: bool) {
1163 let start = kani::any::<Page>();
1164 let end = kani::any::<Page>();
1165 let m = kani::any::<u64>();
1166 let n = kani::any::<u64>();
1167
1168 let offset = m
1171 .checked_add(n)
1172 .and_then(|sum| sum.checked_add(2))
1173 .and_then(|sum| sum.checked_mul(Size4KiB::SIZE));
1174 let expected_start =
1175 offset.and_then(|offset| end.start_address().as_u64().checked_sub(offset));
1176 let should_panic = expected_start.is_some_and(|expected_start| {
1177 expected_start <= 0xffff_7fff_ffff_f000
1178 && end.start_address().as_u64() > 0xffff_7fff_ffff_f000
1179 }) || expected_start.is_none();
1180 kani::assume(should_panic == should_panic_mode);
1181
1182 if should_panic {
1183 let mut our_range = Page::range(start, end);
1185 our_range.nth_back(n as usize);
1186 our_range.nth_back(m as usize);
1187 return;
1188 }
1189
1190 let mut our_range = Page::range(start, end);
1192 let mut native_range = start..end;
1193 assert_eq!(
1194 our_range.nth_back(m as usize),
1195 native_range.nth_back(m as usize)
1196 );
1197 assert_eq!(
1198 our_range.nth_back(n as usize),
1199 native_range.nth_back(n as usize)
1200 );
1201 }
1202
1203 #[kani::proof]
1204 #[kani::unwind(1)]
1205 fn page_range_nth_back() {
1206 page_range_nth_back_harness(false);
1207 }
1208
1209 #[kani::proof]
1210 #[kani::unwind(1)]
1211 #[kani::should_panic]
1212 fn page_range_nth_back_panic() {
1213 page_range_nth_back_harness(true);
1214 }
1215
1216 fn page_range_inclusive_nth_harness(should_panic_mode: bool) {
1217 let start = kani::any::<Page>();
1218 let end = kani::any::<Page>();
1219 let m = kani::any::<u64>();
1220 let n = kani::any::<u64>();
1221
1222 let offset = m
1225 .checked_add(n)
1226 .and_then(|sum| sum.checked_add(2))
1227 .and_then(|sum| sum.checked_mul(Size4KiB::SIZE));
1228 let expected_end =
1229 offset.and_then(|offset| start.start_address().as_u64().checked_add(offset));
1230 let should_panic = expected_end.is_some_and(|expected_end| {
1231 start.start_address().as_u64() <= 0x7fff_ffff_f000 && expected_end > 0x7fff_ffff_f000
1232 }) || expected_end.is_none();
1233 kani::assume(should_panic == should_panic_mode);
1234
1235 if should_panic {
1236 let mut our_range = Page::range_inclusive(start, end);
1238 our_range.nth(n as usize);
1239 our_range.nth(m as usize);
1240 return;
1241 }
1242
1243 let mut our_range = Page::range_inclusive(start, end);
1245 let mut native_range = start..=end;
1246 assert_eq!(our_range.nth(m as usize), native_range.nth(m as usize));
1247 assert_eq!(our_range.nth(n as usize), native_range.nth(n as usize));
1248 }
1249
1250 #[kani::proof]
1251 #[kani::unwind(1)]
1252 fn page_range_inclusive_nth() {
1253 page_range_inclusive_nth_harness(false);
1254 }
1255
1256 #[kani::proof]
1257 #[kani::unwind(1)]
1258 #[kani::should_panic]
1259 fn page_range_inclusive_nth_panic() {
1260 page_range_inclusive_nth_harness(true);
1261 }
1262
1263 fn page_range_inclusive_nth_back_harness(should_panic_mode: bool) {
1264 let start = kani::any::<Page>();
1265 let end = kani::any::<Page>();
1266 let m = kani::any::<u64>();
1267 let n = kani::any::<u64>();
1268
1269 let offset = m
1272 .checked_add(n)
1273 .and_then(|sum| sum.checked_add(2))
1274 .and_then(|sum| sum.checked_mul(Size4KiB::SIZE));
1275 let expected_start =
1276 offset.and_then(|offset| end.start_address().as_u64().checked_sub(offset));
1277 let should_panic = expected_start.is_some_and(|expected_start| {
1278 expected_start <= 0xffff_7fff_ffff_f000
1279 && end.start_address().as_u64() > 0xffff_7fff_ffff_f000
1280 }) || expected_start.is_none();
1281 kani::assume(should_panic == should_panic_mode);
1282
1283 if should_panic {
1284 let mut our_range = Page::range_inclusive(start, end);
1286 our_range.nth_back(n as usize);
1287 our_range.nth_back(m as usize);
1288 return;
1289 }
1290
1291 let mut our_range = Page::range_inclusive(start, end);
1293 let mut native_range = start..=end;
1294 assert_eq!(
1295 our_range.nth_back(m as usize),
1296 native_range.nth_back(m as usize)
1297 );
1298 assert_eq!(
1299 our_range.nth_back(n as usize),
1300 native_range.nth_back(n as usize)
1301 );
1302 }
1303
1304 #[kani::proof]
1305 #[kani::unwind(1)]
1306 fn page_range_inclusive_nth_back() {
1307 page_range_inclusive_nth_back_harness(false);
1308 }
1309
1310 #[kani::proof]
1311 #[kani::unwind(1)]
1312 #[kani::should_panic]
1313 fn page_range_inclusive_nth_back_panic() {
1314 page_range_inclusive_nth_back_harness(true);
1315 }
1316}