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::fmt;
8#[cfg(feature = "step_trait")]
9use core::iter::Step;
10use core::marker::PhantomData;
11use core::ops::{Add, AddAssign, Sub, SubAssign};
12
13/// Trait for abstracting over the three possible page sizes on x86_64, 4KiB, 2MiB, 1GiB.
14pub trait PageSize: Copy + Eq + PartialOrd + Ord + Sealed {
15    /// The page size in bytes.
16    const SIZE: u64;
17
18    /// A string representation of the page size for debug output.
19    const DEBUG_STR: &'static str;
20}
21
22/// This trait is implemented for 4KiB and 2MiB pages, but not for 1GiB pages.
23pub trait NotGiantPageSize: PageSize {}
24
25/// A standard 4KiB page.
26#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash)]
27pub enum Size4KiB {}
28
29/// A “huge” 2MiB page.
30#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash)]
31pub enum Size2MiB {}
32
33/// A “giant” 1GiB page.
34///
35/// (Only available on newer x86_64 CPUs.)
36#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash)]
37pub enum Size1GiB {}
38
39impl PageSize for Size4KiB {
40    const SIZE: u64 = 4096;
41    const DEBUG_STR: &'static str = "4KiB";
42}
43
44impl NotGiantPageSize for Size4KiB {}
45
46impl Sealed for super::Size4KiB {}
47
48impl PageSize for Size2MiB {
49    const SIZE: u64 = Size4KiB::SIZE * 512;
50    const DEBUG_STR: &'static str = "2MiB";
51}
52
53impl NotGiantPageSize for Size2MiB {}
54
55impl Sealed for super::Size2MiB {}
56
57impl PageSize for Size1GiB {
58    const SIZE: u64 = Size2MiB::SIZE * 512;
59    const DEBUG_STR: &'static str = "1GiB";
60}
61
62impl Sealed for super::Size1GiB {}
63
64/// A virtual memory page.
65#[derive(Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash)]
66#[repr(C)]
67pub struct Page<S: PageSize = Size4KiB> {
68    start_address: VirtAddr,
69    size: PhantomData<S>,
70}
71
72impl<S: PageSize> Page<S> {
73    /// The page size in bytes.
74    pub const SIZE: u64 = S::SIZE;
75
76    /// Returns the page that starts at the given virtual address.
77    ///
78    /// Returns an error if the address is not correctly aligned (i.e. is not a valid page start).
79    #[inline]
80    #[rustversion::attr(since(1.61), const)]
81    pub fn from_start_address(address: VirtAddr) -> Result<Self, AddressNotAligned> {
82        if !address.is_aligned_u64(S::SIZE) {
83            return Err(AddressNotAligned);
84        }
85        Ok(Page::containing_address(address))
86    }
87
88    /// Returns the page that starts at the given virtual address.
89    ///
90    /// ## Safety
91    ///
92    /// The address must be correctly aligned.
93    #[inline]
94    #[rustversion::attr(since(1.61), const)]
95    pub unsafe fn from_start_address_unchecked(start_address: VirtAddr) -> Self {
96        Page {
97            start_address,
98            size: PhantomData,
99        }
100    }
101
102    /// Returns the page that contains the given virtual address.
103    #[inline]
104    #[rustversion::attr(since(1.61), const)]
105    pub fn containing_address(address: VirtAddr) -> Self {
106        Page {
107            start_address: address.align_down_u64(S::SIZE),
108            size: PhantomData,
109        }
110    }
111
112    /// Returns the start address of the page.
113    #[inline]
114    #[rustversion::attr(since(1.61), const)]
115    pub fn start_address(self) -> VirtAddr {
116        self.start_address
117    }
118
119    /// Returns the size the page (4KB, 2MB or 1GB).
120    #[inline]
121    #[rustversion::attr(since(1.61), const)]
122    pub fn size(self) -> u64 {
123        S::SIZE
124    }
125
126    /// Returns the level 4 page table index of this page.
127    #[inline]
128    #[rustversion::attr(since(1.61), const)]
129    pub fn p4_index(self) -> PageTableIndex {
130        self.start_address().p4_index()
131    }
132
133    /// Returns the level 3 page table index of this page.
134    #[inline]
135    #[rustversion::attr(since(1.61), const)]
136    pub fn p3_index(self) -> PageTableIndex {
137        self.start_address().p3_index()
138    }
139
140    /// Returns the table index of this page at the specified level.
141    #[inline]
142    #[rustversion::attr(since(1.61), const)]
143    pub fn page_table_index(self, level: PageTableLevel) -> PageTableIndex {
144        self.start_address().page_table_index(level)
145    }
146
147    /// Returns a range of pages, exclusive `end`.
148    #[inline]
149    #[rustversion::attr(since(1.61), const)]
150    pub fn range(start: Self, end: Self) -> PageRange<S> {
151        PageRange { start, end }
152    }
153
154    /// Returns a range of pages, inclusive `end`.
155    #[inline]
156    #[rustversion::attr(since(1.61), const)]
157    pub fn range_inclusive(start: Self, end: Self) -> PageRangeInclusive<S> {
158        PageRangeInclusive { start, end }
159    }
160
161    // FIXME: Move this into the `Step` impl, once `Step` is stabilized.
162    #[cfg(any(feature = "instructions", feature = "step_trait"))]
163    pub(crate) fn steps_between_impl(start: &Self, end: &Self) -> (usize, Option<usize>) {
164        use core::convert::TryFrom;
165
166        if let Some(steps) =
167            VirtAddr::steps_between_u64(&start.start_address(), &end.start_address())
168        {
169            let steps = steps / S::SIZE;
170            let steps = usize::try_from(steps).ok();
171            (steps.unwrap_or(usize::MAX), steps)
172        } else {
173            (0, None)
174        }
175    }
176
177    // FIXME: Move this into the `Step` impl, once `Step` is stabilized.
178    #[cfg(any(feature = "instructions", feature = "step_trait"))]
179    pub(crate) fn forward_checked_impl(start: Self, count: usize) -> Option<Self> {
180        use core::convert::TryFrom;
181
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
327/// A range of pages with exclusive upper bound.
328#[derive(Clone, Copy, PartialEq, Eq, Hash)]
329#[repr(C)]
330pub struct PageRange<S: PageSize = Size4KiB> {
331    /// The start of the range, inclusive.
332    pub start: Page<S>,
333    /// The end of the range, exclusive.
334    pub end: Page<S>,
335}
336
337impl<S: PageSize> PageRange<S> {
338    /// Returns wether this range contains no pages.
339    #[inline]
340    pub fn is_empty(&self) -> bool {
341        self.start >= self.end
342    }
343
344    /// Returns the number of pages in the range.
345    #[inline]
346    pub fn len(&self) -> u64 {
347        if !self.is_empty() {
348            self.end - self.start
349        } else {
350            0
351        }
352    }
353
354    /// Returns the size in bytes of all pages within the range.
355    #[inline]
356    pub fn size(&self) -> u64 {
357        S::SIZE * self.len()
358    }
359}
360
361impl<S: PageSize> Iterator for PageRange<S> {
362    type Item = Page<S>;
363
364    #[inline]
365    fn next(&mut self) -> Option<Self::Item> {
366        if self.start < self.end {
367            let page = self.start;
368            self.start += 1;
369            Some(page)
370        } else {
371            None
372        }
373    }
374}
375
376impl PageRange<Size2MiB> {
377    /// Converts the range of 2MiB pages to a range of 4KiB pages.
378    #[inline]
379    pub fn as_4kib_page_range(self) -> PageRange<Size4KiB> {
380        PageRange {
381            start: Page::containing_address(self.start.start_address()),
382            end: Page::containing_address(self.end.start_address()),
383        }
384    }
385}
386
387impl<S: PageSize> fmt::Debug for PageRange<S> {
388    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
389        f.debug_struct("PageRange")
390            .field("start", &self.start)
391            .field("end", &self.end)
392            .finish()
393    }
394}
395
396/// A range of pages with inclusive upper bound.
397#[derive(Clone, Copy, PartialEq, Eq, Hash)]
398#[repr(C)]
399pub struct PageRangeInclusive<S: PageSize = Size4KiB> {
400    /// The start of the range, inclusive.
401    pub start: Page<S>,
402    /// The end of the range, inclusive.
403    pub end: Page<S>,
404}
405
406impl<S: PageSize> PageRangeInclusive<S> {
407    /// Returns whether this range contains no pages.
408    #[inline]
409    pub fn is_empty(&self) -> bool {
410        self.start > self.end
411    }
412
413    /// Returns the number of frames in the range.
414    #[inline]
415    pub fn len(&self) -> u64 {
416        if !self.is_empty() {
417            self.end - self.start + 1
418        } else {
419            0
420        }
421    }
422
423    /// Returns the size in bytes of all frames within the range.
424    #[inline]
425    pub fn size(&self) -> u64 {
426        S::SIZE * self.len()
427    }
428}
429
430impl<S: PageSize> Iterator for PageRangeInclusive<S> {
431    type Item = Page<S>;
432
433    #[inline]
434    fn next(&mut self) -> Option<Self::Item> {
435        if self.start <= self.end {
436            let page = self.start;
437
438            // If the end of the inclusive range is the maximum page possible for size S,
439            // incrementing start until it is greater than the end will cause an integer overflow.
440            // So instead, in that case we decrement end rather than incrementing start.
441            let max_page_addr = VirtAddr::new(u64::MAX) - (S::SIZE - 1);
442            if self.start.start_address() < max_page_addr {
443                self.start += 1;
444            } else {
445                self.end -= 1;
446            }
447            Some(page)
448        } else {
449            None
450        }
451    }
452}
453
454impl<S: PageSize> fmt::Debug for PageRangeInclusive<S> {
455    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
456        f.debug_struct("PageRangeInclusive")
457            .field("start", &self.start)
458            .field("end", &self.end)
459            .finish()
460    }
461}
462
463/// The given address was not sufficiently aligned.
464#[derive(Debug)]
465pub struct AddressNotAligned;
466
467impl fmt::Display for AddressNotAligned {
468    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
469        write!(f, "the given address was not sufficiently aligned")
470    }
471}
472
473#[cfg(test)]
474mod tests {
475    use super::*;
476
477    fn test_is_hash<T: core::hash::Hash>() {}
478
479    #[test]
480    pub fn test_page_is_hash() {
481        test_is_hash::<Page<Size4KiB>>();
482        test_is_hash::<Page<Size2MiB>>();
483        test_is_hash::<Page<Size1GiB>>();
484    }
485
486    #[test]
487    pub fn test_page_ranges() {
488        let page_size = Size4KiB::SIZE;
489        let number = 1000;
490
491        let start_addr = VirtAddr::new(0xdead_beaf);
492        let start: Page = Page::containing_address(start_addr);
493        let end = start + number;
494
495        let mut range = Page::range(start, end);
496        for i in 0..number {
497            assert_eq!(
498                range.next(),
499                Some(Page::containing_address(start_addr + page_size * i))
500            );
501        }
502        assert_eq!(range.next(), None);
503
504        let mut range_inclusive = Page::range_inclusive(start, end);
505        for i in 0..=number {
506            assert_eq!(
507                range_inclusive.next(),
508                Some(Page::containing_address(start_addr + page_size * i))
509            );
510        }
511        assert_eq!(range_inclusive.next(), None);
512    }
513
514    #[test]
515    pub fn test_page_range_inclusive_overflow() {
516        let page_size = Size4KiB::SIZE;
517        let number = 1000;
518
519        let start_addr = VirtAddr::new(u64::MAX).align_down(page_size) - number * page_size;
520        let start: Page = Page::containing_address(start_addr);
521        let end = start + number;
522
523        let mut range_inclusive = Page::range_inclusive(start, end);
524        for i in 0..=number {
525            assert_eq!(
526                range_inclusive.next(),
527                Some(Page::containing_address(start_addr + page_size * i))
528            );
529        }
530        assert_eq!(range_inclusive.next(), None);
531    }
532
533    #[test]
534    pub fn test_page_range_len() {
535        let start_addr = VirtAddr::new(0xdead_beaf);
536        let start = Page::<Size4KiB>::containing_address(start_addr);
537        let end = start + 50;
538
539        let range = PageRange { start, end };
540        assert_eq!(range.len(), 50);
541
542        let range_inclusive = PageRangeInclusive { start, end };
543        assert_eq!(range_inclusive.len(), 51);
544    }
545
546    #[test]
547    #[cfg(feature = "step_trait")]
548    fn page_step_forward() {
549        let test_cases = [
550            (0, 0, Some(0)),
551            (0, 1, Some(0x1000)),
552            (0x1000, 1, Some(0x2000)),
553            (0x7fff_ffff_f000, 1, Some(0xffff_8000_0000_0000)),
554            (0xffff_8000_0000_0000, 1, Some(0xffff_8000_0000_1000)),
555            (0xffff_ffff_ffff_f000, 1, None),
556            #[cfg(target_pointer_width = "64")]
557            (0x7fff_ffff_f000, 0x1_2345_6789, Some(0xffff_9234_5678_8000)),
558            #[cfg(target_pointer_width = "64")]
559            (0x7fff_ffff_f000, 0x8_0000_0000, Some(0xffff_ffff_ffff_f000)),
560            #[cfg(target_pointer_width = "64")]
561            (0x7fff_fff0_0000, 0x8_0000_00ff, Some(0xffff_ffff_ffff_f000)),
562            #[cfg(target_pointer_width = "64")]
563            (0x7fff_fff0_0000, 0x8_0000_0100, None),
564            #[cfg(target_pointer_width = "64")]
565            (0x7fff_ffff_f000, 0x8_0000_0001, None),
566            // Make sure that we handle `steps * PAGE_SIZE > u32::MAX`
567            // correctly on 32-bit targets.
568            (0, 0x10_0000, Some(0x1_0000_0000)),
569        ];
570        for (start, count, result) in test_cases {
571            let start = Page::<Size4KiB>::from_start_address(VirtAddr::new(start)).unwrap();
572            let result = result
573                .map(|result| Page::<Size4KiB>::from_start_address(VirtAddr::new(result)).unwrap());
574            assert_eq!(Step::forward_checked(start, count), result);
575        }
576    }
577
578    #[test]
579    #[cfg(feature = "step_trait")]
580    fn page_step_backwards() {
581        let test_cases = [
582            (0, 0, Some(0)),
583            (0, 1, None),
584            (0x1000, 1, Some(0)),
585            (0xffff_8000_0000_0000, 1, Some(0x7fff_ffff_f000)),
586            (0xffff_8000_0000_1000, 1, Some(0xffff_8000_0000_0000)),
587            #[cfg(target_pointer_width = "64")]
588            (0xffff_9234_5678_8000, 0x1_2345_6789, Some(0x7fff_ffff_f000)),
589            #[cfg(target_pointer_width = "64")]
590            (0xffff_8000_0000_0000, 0x8_0000_0000, Some(0)),
591            #[cfg(target_pointer_width = "64")]
592            (0xffff_8000_0000_0000, 0x7_ffff_ff01, Some(0xff000)),
593            #[cfg(target_pointer_width = "64")]
594            (0xffff_8000_0000_0000, 0x8_0000_0001, None),
595            // Make sure that we handle `steps * PAGE_SIZE > u32::MAX`
596            // correctly on 32-bit targets.
597            (0x1_0000_0000, 0x10_0000, Some(0)),
598        ];
599        for (start, count, result) in test_cases {
600            let start = Page::<Size4KiB>::from_start_address(VirtAddr::new(start)).unwrap();
601            let result = result
602                .map(|result| Page::<Size4KiB>::from_start_address(VirtAddr::new(result)).unwrap());
603            assert_eq!(Step::backward_checked(start, count), result);
604        }
605    }
606
607    #[test]
608    #[cfg(feature = "step_trait")]
609    fn page_steps_between() {
610        let test_cases = [
611            (0, 0, 0, Some(0)),
612            (0, 0x1000, 1, Some(1)),
613            (0x1000, 0, 0, None),
614            (0x1000, 0x1000, 0, Some(0)),
615            (0x7fff_ffff_f000, 0xffff_8000_0000_0000, 1, Some(1)),
616            (0xffff_8000_0000_0000, 0x7fff_ffff_f000, 0, None),
617            (0xffff_8000_0000_0000, 0xffff_8000_0000_0000, 0, Some(0)),
618            (0xffff_8000_0000_0000, 0xffff_8000_0000_1000, 1, Some(1)),
619            (0xffff_8000_0000_1000, 0xffff_8000_0000_0000, 0, None),
620            (0xffff_8000_0000_1000, 0xffff_8000_0000_1000, 0, Some(0)),
621            // Make sure that we handle `steps * PAGE_SIZE > u32::MAX` correctly on 32-bit
622            // targets.
623            (
624                0x0000_0000_0000,
625                0x0001_0000_0000,
626                0x10_0000,
627                Some(0x10_0000),
628            ),
629            // The returned bounds are different when `steps` doesn't fit in
630            // into `usize`. On 64-bit targets, `0x1_0000_0000` fits into
631            // `usize`, so we can return exact lower and upper bounds. On
632            // 32-bit targets, `0x1_0000_0000` doesn't fit into `usize`, so we
633            // only return an lower bound of `usize::MAX` and don't return an
634            // upper bound.
635            #[cfg(target_pointer_width = "64")]
636            (
637                0x0000_0000_0000,
638                0x1000_0000_0000,
639                0x1_0000_0000,
640                Some(0x1_0000_0000),
641            ),
642            #[cfg(not(target_pointer_width = "64"))]
643            (0x0000_0000_0000, 0x1000_0000_0000, usize::MAX, None),
644        ];
645        for (start, end, lower, upper) in test_cases {
646            let start = Page::<Size4KiB>::from_start_address(VirtAddr::new(start)).unwrap();
647            let end = Page::from_start_address(VirtAddr::new(end)).unwrap();
648            assert_eq!(Step::steps_between(&start, &end), (lower, upper));
649        }
650    }
651}