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
138
139
140
141
142
143
use super::{Frame, FrameAllocator, FrameSize, PAddr, Page, VAddr};
use core::{
    fmt,
    ops::{self, Range},
};

/// Defines the permissions for a region of memory. Used both for abstract regions of memory (e.g. entries in a
/// memory map) and as a architecture-common representation of paging structures.
///
/// The `Add` implementation "coalesces" two sets of `Flags`, giving a set of `Flags` that has the permissions of
/// both of the sets. For example, if one region is writable and the other is not, the coalesced flags will be
/// writable. By default, a region is considered to be cached, so coalesced flags will only be cached if both input
/// regions can safely be cached.
#[derive(Clone, Copy, PartialEq, Eq, Debug)]
pub struct Flags {
    pub writable: bool,
    pub executable: bool,
    pub user_accessible: bool,
    pub cached: bool,
}

impl Default for Flags {
    fn default() -> Self {
        Flags { writable: false, executable: false, user_accessible: false, cached: true }
    }
}

impl ops::Add for Flags {
    type Output = Self;

    fn add(self, other: Self) -> Self::Output {
        Flags {
            writable: self.writable || other.writable,
            executable: self.executable || other.executable,
            user_accessible: self.user_accessible || other.user_accessible,
            // If either of the regions should not be cached, we can't cache any of it
            cached: self.cached && other.cached,
        }
    }
}

#[derive(Debug)]
pub enum PagingError {
    /// The virtual memory that is being mapped is already mapped to another part of physical memory.
    AlreadyMapped,
}

/// A `PageTable` allows the manipulation of a set of page-tables.
// TODO: think about how we can do versatile unmapping (maybe return a `Map` type that is returned to unmap - this
// could store information needed to unmap an artitrarily-mapped area).
pub trait PageTable<TableSize>: Sized + fmt::Debug
where
    TableSize: FrameSize,
{
    /// Constructs a new set of page tables, but with the kernel mapped into it. This is generally useful for
    /// constructing page tables for userspace.
    fn new_with_kernel_mapped<A>(kernel_page_table: &Self, allocator: &A) -> Self
    where
        A: FrameAllocator<TableSize>;

    /// Install these page tables as the current set.
    unsafe fn switch_to(&self);

    /// Get the physical address that a given virtual address is mapped to, if it's mapped. Returns `None` if the
    /// address is not mapped into physical memory.
    fn translate(&self, address: VAddr) -> Option<PAddr>;

    /// Map a `Page` to a `Frame` with the given flags.
    fn map<S, A>(
        &mut self,
        page: Page<S>,
        frame: Frame<S>,
        flags: Flags,
        allocator: &A,
    ) -> Result<(), PagingError>
    where
        S: FrameSize,
        A: FrameAllocator<TableSize>;

    /// Map each `Page` in a range to a corresponding `Frame` with the given flags.
    fn map_range<S, A>(
        &mut self,
        pages: Range<Page<S>>,
        frames: Range<Frame<S>>,
        flags: Flags,
        allocator: &A,
    ) -> Result<(), PagingError>
    where
        S: FrameSize,
        A: FrameAllocator<TableSize>,
    {
        for (page, frame) in pages.zip(frames) {
            self.map(page, frame, flags, allocator)?;
        }

        Ok(())
    }

    /// Map an area of `size` bytes starting at the given address pair with the given flags. Implementations are
    /// free to map this area however they desire, and may do so with a range of page sizes.
    fn map_area<A>(
        &mut self,
        // memory_type: MemoryType,
        virtual_start: VAddr,
        physical_start: PAddr,
        size: usize,
        flags: Flags,
        allocator: &A,
    ) -> Result<(), PagingError>
    where
        A: FrameAllocator<TableSize>;

    fn unmap<S>(&mut self, page: Page<S>) -> Option<Frame<S>>
    where
        S: FrameSize;
}

#[cfg(test)]
mod tests {
    use super::*;
    use crate::memory::{Size1GiB, Size2MiB, Size4KiB};

    #[test]
    fn test_flag_coalescing() {
        assert_eq!(Flags::default() + Flags::default(), Flags::default());
        assert_eq!(
            Flags::default() + Flags { writable: false, executable: true, user_accessible: true, cached: true },
            Flags { writable: false, executable: true, user_accessible: true, cached: true }
        );
        assert_eq!(
            Flags::default() + Flags { writable: true, executable: true, user_accessible: true, cached: true },
            Flags { writable: true, executable: true, user_accessible: true, cached: true }
        );
        assert_eq!(
            Flags::default() + Flags { cached: false, ..Default::default() },
            Flags { cached: false, ..Default::default() }
        );
        assert_eq!(
            Flags { cached: false, ..Default::default() } + Flags { cached: false, ..Default::default() },
            Flags { cached: false, ..Default::default() }
        );
    }
}