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
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
//! Virtual memory management.
//! 
//! Page table fields have cryptic names. They are as follow:
//! 
//!  * D - set to 1 on write
//!  * PS - combine with CR4.PAE and EFER.LMA to determine page size
//!  * G - if 1, TLB for the page is not invalidated on load to CR3 or task switch
//!  * AVL - free for software use
//!  * PAT - page attribute table something-or-other
//!  * NX - prohibit execution from page if 1 and EFER.NXE=1
//!  * A - set to 1 on access
//!  * PCD - non-cachable if 1
//!  * PWT - writeback caching if 0, otherwise write-through
//!  * U/S - not readable by CPL3 if 0
//!  * R/W - read-only if 0
//!  * P - next level exists in physical memory (#PF on access if 0)
//!
//! The page table structure (on x86_64) is PML4 -> PDPT -> PDT -> PT. Each
//! level is an array of 512 entries at a 4k-aligned physical address. PDT
//! may be omitted in which case the corresponding PDPT entry refers to a 1GB
//! physical page, or PT may be omitted in which case the corresponding PDT
//! entry refers to a 2MB physical page.

use super::PhysPtr;

/// Declare a struct describing a page table entry.
///
/// These entries are assumed to have A, PCD, PWT, U/S and R/W bits.
macro_rules! pt_decl {
    ($name:ident) => (
        pub struct $name(u64);
        impl AsMut<u64> for $name {
            fn as_mut(&mut self) -> &mut u64 {
                &mut self.0
            }
        }
        impl HasPCD for $name {}
        impl HasUS for $name {}
        impl HasRW for $name {}
        impl HasA for $name {}
    )
}

#[inline]
fn write_bits(x: &mut u64, nbits: u8, shift: u8, value: u64) {
    let mut mask = (1 << nbits) - 1;
    // value must not have any bits set beyond the low `nbits` bits.
    assert!(value & !mask == 0);

    mask <<= shift;
    *x &= !mask;
    *x |= value << shift;
}

/// PML4 entry
pt_decl!(PML4E);
/// PDPT entry
pt_decl!(PDPE);
/// PDT entry
pt_decl!(PDE);
/// PT entry
pt_decl!(PTE);

/// Top-level page table, corresponding to bits 39 through 47 of a virtual address
pub type PML4 = [PML4E; 512];
/// Third-level page table, corresponding to bits 30 through 38 of a virtual address
pub type PDP = [PDPE; 512];
/// Second-level page table, bits 21 through 29 of a virtual address.
pub type PD = [PDE; 512];
/// Bottom-level page table, bits 12 through 20 of a virtual address.
pub type PT = [PTE; 512];

/// Encodings for PCD and PWT bits.
pub enum CachePolicy {
    /// May not be cached.
    NonCacheable = 2,
    /// Write-back cached.
    WriteBack = 0,
    /// Write-through cached.
    WriteThrough = 1
}

/// A page table level with PCD and PWT bits.
trait HasPCD: AsMut<u64> {
    /// Set the PCD and PWT bits.
    fn set_cache_policy(&mut self, policy: CachePolicy) {
        write_bits(self.as_mut(), 2, 3, policy as u64);
    }
}

/// A page table level with a U/S bit.
trait HasUS: AsMut<u64> {
    /// Set the U/S bit.
    fn set_supervisor_only(&mut self, s_only: bool) {
        write_bits(self.as_mut(), 2, 1, if s_only {
            0
        } else {
            1
        });
    }
}

/// A page table level with a R/W bit.
trait HasRW: AsMut<u64> {
    /// Set the R/W bit.
    fn set_writable(&mut self, writable: bool) {
        write_bits(self.as_mut(), 1, 1, if writable {
            1
        } else {
            0
        });
    }
}

/// A page table level with a A bit.
trait HasA: AsMut<u64> { }

impl PML4E {
    pub fn new(pdp: Option<PhysPtr<PDP>>) -> PML4E {
        PML4E(match pdp {
            // Not present
            None => 0,
            Some(addr) => {
                assert_aligned!(addr.0, 4096);
                // Present, PDP base address
                1 | addr.0 as u64
            }
        })
    }
}

/// Contents of the memory region described by a `PDPE` or `PDE`.
pub enum DirEntry<T> {
    /// Physical page.
    Page(PhysPtr<()>),
    /// Next-level page directory.
    Directory(PhysPtr<T>)
}

impl PDPE {
    pub fn new(contents: Option<DirEntry<PD>>) -> PDPE {
        PDPE(match contents {
            Some(DirEntry::Page(addr)) => {
                assert_aligned!(addr.0, 1 << 20);
                // Present, bit 7 indicates 1G page
                0x81 | addr.0 as u64
            },
            Some(DirEntry::Directory(addr)) => {
                assert_aligned!(addr.0 as u64, 4096);
                // Present
                1 | addr.0 as u64
            },
            // Not present
            None => 0
        })
    }
}

impl PDE {
    pub fn new(contents: Option<DirEntry<PT>>) -> PDE {
        unimplemented!()
    }
}

/// Set the page table root (PML4).
unsafe fn set_pt_root(addr: PhysPtr<PML4>) {
    unimplemented!()
}

/// Get the current page table root.
unsafe fn get_pt_root() -> PhysPtr<PML4> {
    unimplemented!()
}