1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
//! Defines the structure of Page Table Entries (PTEs) on x86_64.
//!
//! This crate is also useful for frame deallocation, because it can determine
//! when a frame is mapped exclusively to only one page table entry,
//! and therefore when it is safe to deallocate.
//!
//! Because Theseus ensures a bijective (1-to-1) mapping
//! between virtual pages and physical frames,
//! it is almost always the case that the frame pointed to
//! by a newly-unmapped page table entry can be deallocated.
//!

#![no_std]

use core::ops::Deref;
use memory_structs::{Frame, FrameRange, PhysicalAddress, PageSize};
use zerocopy::FromBytes;
use frame_allocator::AllocatedFrame;
use pte_flags::{PteFlagsArch, PTE_FRAME_MASK};

/// A page table entry, which is a `u64` value under the hood.
///
/// It contains a the physical address of the `Frame` being mapped by this entry
/// and the access bits (encoded `PteFlags`) that describes how it's mapped,
/// e.g., readable, writable, no exec, etc.
///
/// There isn't and shouldn't be any way to create/instantiate a new `PageTableEntry` directly.
/// You can only obtain a reference to an `PageTableEntry` by going through a page table's `Table` struct itself.
#[derive(FromBytes)]
#[repr(transparent)]
pub struct PageTableEntry(u64);

impl PageTableEntry {
    /// Returns `true` if this entry is unused, i.e., cleared/zeroed out.
    pub fn is_unused(&self) -> bool {
        self.0 == 0
    }

    /// Zeroes out this entry, setting it as "unused".
    pub fn zero(&mut self) {
        self.0 = 0;
    }

    /// Removes the mapping represented by this page table entry.
    ///
    /// If the frame(s) pointed to by this entry were mapped exlusively,
    /// i.e., owned by this entry and not mapped anywhere else by any other entries,
    /// then this function returns those frames.
    /// This is useful because those returned frames can then be safely deallocated.
    pub fn set_unmapped(&mut self) -> UnmapResult {
        let frame = self.frame_value();
        let flags = self.flags();
        self.zero();

        // Since we don't support huge pages, this PTE can only cover one 4KiB frame.
        // Once we support huge pages, we can use a type parameter
        // to specify whether this is a 4KiB, 2MiB, or 1GiB PTE.
        let frame_range = FrameRange::new(frame, frame);
        if flags.is_exclusive() {
            UnmapResult::Exclusive(UnmappedFrameRange(frame_range))
        } else {
            UnmapResult::NonExclusive(frame_range)
        }
    }

    /// Returns this `PageTableEntry`'s flags.
    pub fn flags(&self) -> PteFlagsArch {
        PteFlagsArch::from_bits_truncate(self.0 & !PTE_FRAME_MASK)
    }

    /// Returns the physical `Frame` pointed to (mapped by) this `PageTableEntry`.
    /// If this page table entry is not `PRESENT`, this returns `None`.
    pub fn pointed_frame(&self) -> Option<Frame> {
        if self.flags().is_valid() {
            Some(self.frame_value())
        } else {
            None
        }
    }

    /// Extracts the value of the frame referred to by this page table entry.
    fn frame_value(&self) -> Frame {
        let mut frame_paddr = self.0 as usize;
        frame_paddr &= PTE_FRAME_MASK as usize;
        Frame::containing_address(PhysicalAddress::new_canonical(frame_paddr))
    }

    /// Sets this `PageTableEntry` to map the given `frame` with the given `flags`.
    ///
    /// This is the actual mapping action that informs the MMU of a new mapping.
    ///
    /// Note: this performs no checks about the current value of this page table entry.
    pub fn set_entry<P: PageSize>(&mut self, frame: AllocatedFrame<P>, flags: PteFlagsArch) {
        self.0 = (frame.start_address().value() as u64) | flags.bits();
    }

    /// Sets the flags components of this `PageTableEntry` to `new_flags`.
    ///
    /// This does not modify the frame part of the page table entry.
    pub fn set_flags(&mut self, new_flags: PteFlagsArch) {
        let only_flag_bits = new_flags.bits() & !PTE_FRAME_MASK;
        self.0 = (self.0 & PTE_FRAME_MASK) | only_flag_bits;
    }

    pub fn value(&self) -> u64 {
        self.0
    }
}

/// The frames returned from the action of unmapping a page table entry.
/// See the `PageTableEntry::set_unmapped()` function.
///
/// If exclusive, the contained `UnmappedFrameRange` can be used to deallocate frames.
///
/// If non-exclusive, the contained `FrameRange` is provided just for debugging feedback.
/// Note that we use `FrameRange` instead of `Frame` because a single page table entry
/// can map many frames, e.g., using huge pages.
#[must_use]
pub enum UnmapResult {
    Exclusive(UnmappedFrameRange),
    NonExclusive(FrameRange)
}

/// A range of frames that have been unmapped from a `PageTableEntry`
/// that previously mapped that frame exclusively (i.e., "owned it").
///
/// `UnmappedFrameRange` can be used to create an `UnmappedFrames`
/// and then safely deallocated within the `frame_allocator`.
///
/// See the `PageTableEntry::set_unmapped()` function.
pub struct UnmappedFrameRange(FrameRange);
impl Deref for UnmappedFrameRange {
    type Target = FrameRange;
    fn deref(&self) -> &FrameRange {
        &self.0
    }
}