Skip to main content

x86_64/structures/paging/
page.rs

1//! Abstractions for default-sized and huge virtual memory pages.
2
3use 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
14/// Trait for abstracting over the three possible page sizes on x86_64, 4KiB, 2MiB, 1GiB.
15pub trait PageSize: Copy + Eq + PartialOrd + Ord + Sealed {
16    /// The page size in bytes.
17    const SIZE: u64;
18
19    /// A string representation of the page size for debug output.
20    const DEBUG_STR: &'static str;
21}
22
23/// This trait is implemented for 4KiB and 2MiB pages, but not for 1GiB pages.
24pub trait NotGiantPageSize: PageSize {}
25
26/// A standard 4KiB page.
27#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash)]
28pub enum Size4KiB {}
29
30/// A “huge” 2MiB page.
31#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash)]
32pub enum Size2MiB {}
33
34/// A “giant” 1GiB page.
35///
36/// (Only available on newer x86_64 CPUs.)
37#[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/// A virtual memory page.
66#[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    /// The page size in bytes.
75    pub const SIZE: u64 = S::SIZE;
76
77    /// Returns the page that starts at the given virtual address.
78    ///
79    /// Returns an error if the address is not correctly aligned (i.e. is not a valid page start).
80    #[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    /// Returns the page that starts at the given virtual address.
90    ///
91    /// ## Safety
92    ///
93    /// The address must be correctly aligned.
94    #[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    /// Returns the page that contains the given virtual address.
104    #[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    /// Returns the start address of the page.
114    #[inline]
115    #[rustversion::attr(since(1.61), const)]
116    pub fn start_address(self) -> VirtAddr {
117        self.start_address
118    }
119
120    /// Returns the size the page (4KB, 2MB or 1GB).
121    #[inline]
122    #[rustversion::attr(since(1.61), const)]
123    pub fn size(self) -> u64 {
124        S::SIZE
125    }
126
127    /// Returns the level 4 page table index of this page.
128    #[inline]
129    #[rustversion::attr(since(1.61), const)]
130    pub fn p4_index(self) -> PageTableIndex {
131        self.start_address().p4_index()
132    }
133
134    /// Returns the level 3 page table index of this page.
135    #[inline]
136    #[rustversion::attr(since(1.61), const)]
137    pub fn p3_index(self) -> PageTableIndex {
138        self.start_address().p3_index()
139    }
140
141    /// Returns the table index of this page at the specified level.
142    #[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    /// Returns a range of pages, exclusive `end`.
149    #[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    /// Returns a range of pages, inclusive `end`.
156    #[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    // FIXME: Move this into the `Step` impl, once `Step` is stabilized.
163    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    // FIXME: Move this into the `Step` impl, once `Step` is stabilized.
169    #[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    // FIXME: Move this into the `Step` impl, once `Step` is stabilized.
180    #[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    /// Returns the level 2 page table index of this page.
193    #[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    /// Returns the 1GiB memory page with the specified page table indices.
202    #[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    /// Returns the 2MiB memory page with the specified page table indices.
217    #[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    /// Returns the 4KiB memory page with the specified page table indices.
234    #[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    /// Returns the level 1 page table index of this page.
251    #[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    // Kani's bundled toolchain predates these methods being added to `Step`.
327    // Exclude them there so the crate still compiles under `cargo kani`.
328    // This can be removed once Kani upgrades its bundled toolchain to nightly-2026-07-10 or later.
329    #[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    // Kani's bundled toolchain predates these methods being added to `Step`.
338    // Exclude them there so the crate still compiles under `cargo kani`.
339    // This can be removed once Kani upgrades its bundled toolchain to nightly-2026-07-10 or later.
340    #[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/// A range of pages with exclusive upper bound.
350#[derive(Clone, Copy, PartialEq, Eq, Hash)]
351#[repr(C)]
352pub struct PageRange<S: PageSize = Size4KiB> {
353    /// The start of the range, inclusive.
354    pub start: Page<S>,
355    /// The end of the range, exclusive.
356    pub end: Page<S>,
357}
358
359impl<S: PageSize> PageRange<S> {
360    /// Returns whether this range contains no pages.
361    #[inline]
362    pub fn is_empty(&self) -> bool {
363        self.start >= self.end
364    }
365
366    /// Returns the number of pages in the range.
367    #[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    /// Returns the size in bytes of all pages within the range.
377    #[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        // Convert to `u64`. If the value doesn't fit just use `u64::MAX`.
403        // `self.len()` is guaranteed to be smaller than the real value and
404        // `u64::MAX` anyway, so it doesn't make a difference.
405        let n = u64::try_from(n).unwrap_or(u64::MAX);
406
407        // Handling `n >= self.len()` is a bit more complicated because we
408        // can't just add `n` to `self.start`. Handle this by doing two steps,
409        // `self.len()-1` and `1`. This should return `None` (or panic).
410        if n >= self.len() {
411            self.nth(self.len() as usize - 1)?;
412            return self.next();
413        }
414
415        // Figure out how many steps there are until the address range gap.
416        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            // Jump just *before* the address range gap.
421            self.start += steps_until_gap - 1;
422            // Advancing one more time should panic.
423            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        // Convert to `u64`. If the value doesn't fit just use `u64::MAX`.
456        // `self.len()` is guaranteed to be smaller than the real value and
457        // `u64::MAX` anyway, so it doesn't make a difference.
458        let n = u64::try_from(n).unwrap_or(u64::MAX);
459
460        // Handling `n >= self.len()` is a bit more complicated because we
461        // can't just subtract `n` from `self.end`. Handle this by doing two
462        // steps, `self.len()-1` and `1`. This should return `None` (or panic).
463        if n >= self.len() {
464            self.nth_back(self.len() as usize - 1);
465            return self.next_back();
466        }
467
468        // Figure out how many steps there are until the address range gap.
469        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            // Jump just *before* the address range gap.
474            self.end -= steps_until_gap - 1;
475            // Advancing one more time should panic.
476            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    /// Converts the range of 2MiB pages to a range of 4KiB pages.
487    #[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/// A range of pages with inclusive upper bound.
506#[derive(Clone, Copy, PartialEq, Eq, Hash)]
507#[repr(C)]
508pub struct PageRangeInclusive<S: PageSize = Size4KiB> {
509    /// The start of the range, inclusive.
510    pub start: Page<S>,
511    /// The end of the range, inclusive.
512    pub end: Page<S>,
513}
514
515impl<S: PageSize> PageRangeInclusive<S> {
516    /// Returns whether this range contains no pages.
517    #[inline]
518    pub fn is_empty(&self) -> bool {
519        self.start > self.end
520    }
521
522    /// Returns the number of pages in the range.
523    #[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    /// Returns the size in bytes of all pages within the range.
533    #[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            // If the end of the inclusive range is the maximum page possible for size S,
548            // incrementing start until it is greater than the end will cause an integer overflow.
549            // So instead, in that case we decrement end rather than incrementing start.
550            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        // Convert to `u64`. If the value doesn't fit just use `u64::MAX`.
568        // `self.len()` is guaranteed to be smaller than the real value and
569        // `u64::MAX` anyway, so it doesn't make a difference.
570        let n = u64::try_from(n).unwrap_or(u64::MAX);
571
572        // Handling `n >= self.len()` is a bit more complicated because we
573        // can't just add `n` to `self.start`. Handle this by doing two steps,
574        // `self.len()-1` and `1`. This should return `None` (or panic).
575        if n >= self.len() {
576            self.nth(self.len() as usize - 1)?;
577            return self.next();
578        }
579
580        // Figure out how many steps there are until the address range gap.
581        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            // Jump just *before* the address range gap.
586            self.start += steps_until_gap - 1;
587            // Advancing one more time should panic.
588            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 the start of the inclusive range is 0, decrementing end until
611            // it is smaller than the start will cause an integer underflow.
612            // So instead, in that case we increment start rather than decrementing end.
613            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        // Convert to `u64`. If the value doesn't fit just use `u64::MAX`.
630        // `self.len()` is guaranteed to be smaller than the real value and
631        // `u64::MAX` anyway, so it doesn't make a difference.
632        let n = u64::try_from(n).unwrap_or(u64::MAX);
633
634        // Handling `n >= self.len()` is a bit more complicated because we
635        // can't just subtract `n` from `self.end`. Handle this by doing two
636        // steps, `self.len()-1` and `1`. This should return `None` (or panic).
637        if n >= self.len() {
638            self.nth_back(self.len() as usize - 1);
639            return self.next_back();
640        }
641
642        // Figure out how many steps there are until the address range gap.
643        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            // Jump just *before* the address range gap.
648            self.end -= steps_until_gap - 1;
649            // Advancing one more time should panic.
650            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/// The given address was not sufficiently aligned.
676#[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    // TODO: This probably shouldn't panic, but we can't fix this without a breaking change.
758    #[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    // TODO: This probably shouldn't panic, but we can't fix this without a breaking change.
795    #[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            // Make sure that we handle `steps * PAGE_SIZE > u32::MAX`
855            // correctly on 32-bit targets.
856            (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            // Make sure that we handle `steps * PAGE_SIZE > u32::MAX`
884            // correctly on 32-bit targets.
885            (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            // Make sure that we handle `steps * PAGE_SIZE > u32::MAX` correctly on 32-bit
910            // targets.
911            (
912                0x0000_0000_0000,
913                0x0001_0000_0000,
914                0x10_0000,
915                Some(0x10_0000),
916            ),
917            // The returned bounds are different when `steps` doesn't fit in
918            // into `usize`. On 64-bit targets, `0x1_0000_0000` fits into
919            // `usize`, so we can return exact lower and upper bounds. On
920            // 32-bit targets, `0x1_0000_0000` doesn't fit into `usize`, so we
921            // only return an lower bound of `usize::MAX` and don't return an
922            // upper bound.
923            #[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        // If the code is expected to panic, only run it in `#[should_panic]`
968        // mode.
969        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            // Calling `next` should panic.
975            let mut our_range = Page::range(start, end);
976            our_range.next();
977            our_range.next();
978            return;
979        }
980
981        // Otherwise the results should match what `Range` returns.
982        let mut our_range = Page::range(start, end);
983        let mut native_range = start..end;
984        // The first assert checks that we're returning the correct value.
985        assert_eq!(our_range.next(), native_range.next());
986        // The second assert checks that we're updating the range state correctly.
987        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        // If the code is expected to panic, only run it in `#[should_panic]`
1006        // mode.
1007        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            // Calling `next_back` should panic.
1013            let mut our_range = Page::range(start, end);
1014            our_range.next_back();
1015            our_range.next_back();
1016            return;
1017        }
1018
1019        // Otherwise the results should match what `Range` returns.
1020        let mut our_range = Page::range(start, end);
1021        let mut native_range = start..end;
1022        // The first assert checks that we're returning the correct value.
1023        assert_eq!(our_range.next_back(), native_range.next_back());
1024        // The second assert checks that we're updating the range state correctly.
1025        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        // If the code is expected to panic, only run it in `#[should_panic]`
1044        // mode.
1045        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            // Calling `next` should panic.
1051            let mut our_range = Page::range_inclusive(start, end);
1052            our_range.next();
1053            our_range.next();
1054            return;
1055        }
1056
1057        // Otherwise the results should match what `Range` returns.
1058        let mut our_range = Page::range_inclusive(start, end);
1059        let mut native_range = start..=end;
1060        // The first assert checks that we're returning the correct value.
1061        assert_eq!(our_range.next(), native_range.next());
1062        // The second assert checks that we're updating the range state correctly.
1063        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        // If the code is expected to panic, only run it in `#[should_panic]`
1082        // mode.
1083        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            // Calling `next_back` should panic.
1089            let mut our_range = Page::range_inclusive(start, end);
1090            our_range.next_back();
1091            our_range.next_back();
1092            return;
1093        }
1094
1095        // Otherwise the results should match what `Range` returns.
1096        let mut our_range = Page::range_inclusive(start, end);
1097        let mut native_range = start..=end;
1098        // The first assert checks that we're returning the correct value.
1099        assert_eq!(our_range.next_back(), native_range.next_back());
1100        // The second assert checks that we're updating the range state correctly.
1101        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        // If the code is expected to panic, only run it in `#[should_panic]`
1122        // mode.
1123        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            // Calling `nth` should panic.
1136            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        // Otherwise the results should match what `Range` returns.
1143        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        // If the code is expected to panic, only run it in `#[should_panic]`
1169        // mode.
1170        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            // Calling `nth_back` should panic.
1184            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        // Otherwise the results should match what `Range` returns.
1191        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        // If the code is expected to panic, only run it in `#[should_panic]`
1223        // mode.
1224        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            // Calling `nth` should panic.
1237            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        // Otherwise the results should match what `Range` returns.
1244        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        // If the code is expected to panic, only run it in `#[should_panic]`
1270        // mode.
1271        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            // Calling `nth_back` should panic.
1285            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        // Otherwise the results should match what `Range` returns.
1292        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}