use super::*;
#[repr(align(4096))]
pub struct PageTable {
pub entries: [Entry; 512],
}
#[derive(Debug, Clone, Copy)]
pub struct Entry {
entry: u64,
}
#[repr(u64)]
#[derive(Debug, Clone, Copy)]
pub enum Bit {
Present = 0,
Writable = 1,
User = 2,
Direct = 3,
Nocache = 4,
Accessed = 5,
Dirty = 6,
Huge = 7,
Global = 8,
Noexec = 63,
}
add_indexing!(PageTable, Entry);
impl PageTable {
pub const fn new() -> Self {
PageTable {
entries: [Entry::new(); 512],
}
}
pub fn clear(&mut self) {
for entry in self.entries.iter_mut() {
entry.clear();
}
}
}
impl Entry {
pub const fn new() -> Self {
Entry {
entry: 0,
}
}
fn read(&self) -> u64 {
let entry_ptr = &self.entry as *const u64;
unsafe { core::ptr::read_volatile(entry_ptr) }
}
fn write(&mut self, entry: u64) {
let entry_ptr = &mut self.entry as *mut u64;
unsafe { core::ptr::write_volatile(entry_ptr, entry); }
}
fn update<F>(&mut self, f: F) where F: Fn(u64) -> u64 {
self.write(f(self.read()));
}
pub fn clear(&mut self) {
self.write(0);
}
pub fn address(&self) -> PhysicalAddress {
self.read() & 0x000ffffffffff000
}
pub fn set_address(&mut self, address: PhysicalAddress) -> &mut Self {
assert!(address % 0x1000 == 0);
assert!(address < 0xfff0_0000_0000_0000);
self.update(|entry| {
(entry & !0x000ffffffffff000) | address
});
self
}
pub fn avail(&self) -> u8 {
((self.read() & 0x0e00) >> 9) as u8
}
pub fn set_avail(&mut self, val: u8) -> &mut Self {
if val > 7 {
panic!("Avail value out ouf bounds");
}
self.update(|entry| {
(entry & !0x0e00) | ((val as u64) << 9)
});
self
}
pub fn bit(&self, bit: Bit) -> bool {
get_bit!(self.read(), bit as u64)
}
fn modify_bit(&mut self, bit: Bit, val: bool) {
self.update(|mut entry| {
set_bit!(entry, bit as u64, val);
entry
});
}
pub fn set_bit(&mut self, bit: Bit) -> &mut Self {
self.modify_bit(bit, true);
self
}
pub fn unset_bit(&mut self, bit: Bit) -> &mut Self {
self.modify_bit(bit, false);
self
}
}
#[test]
fn int_consistency() {
let mut entry = Entry::new();
entry.set_address(0x4242000);
assert_eq!(entry.address(), 0x4242000);
entry.set_avail(7);
assert_eq!(entry.avail(), 7);
entry.set_avail(0);
assert_eq!(entry.avail(), 0);
}
#[test]
fn bit_consistency() {
let mut entry = Entry::new();
entry.set_address(0x000ffffffffff000);
assert!(!entry.bit(Bit::Present));
assert!(!entry.bit(Bit::Writable));
assert!(!entry.bit(Bit::User));
assert!(!entry.bit(Bit::Direct));
assert!(!entry.bit(Bit::Nocache));
assert!(!entry.bit(Bit::Accessed));
assert!(!entry.bit(Bit::Dirty));
assert!(!entry.bit(Bit::Huge));
assert!(!entry.bit(Bit::Global));
assert!(!entry.bit(Bit::Noexec));
entry.set_bit(Bit::Present);
assert!(entry.bit(Bit::Present));
entry.unset_bit(Bit::Present);
entry.set_bit(Bit::Writable);
assert!(entry.bit(Bit::Writable));
entry.unset_bit(Bit::Writable);
entry.set_bit(Bit::User);
assert!(entry.bit(Bit::User));
entry.unset_bit(Bit::User);
entry.set_bit(Bit::Direct);
assert!(entry.bit(Bit::Direct));
entry.unset_bit(Bit::Direct);
entry.set_bit(Bit::Nocache);
assert!(entry.bit(Bit::Nocache));
entry.unset_bit(Bit::Nocache);
entry.set_bit(Bit::Accessed);
assert!(entry.bit(Bit::Accessed));
entry.unset_bit(Bit::Accessed);
entry.set_bit(Bit::Dirty);
assert!(entry.bit(Bit::Dirty));
entry.unset_bit(Bit::Dirty);
entry.set_bit(Bit::Huge);
assert!(entry.bit(Bit::Huge));
entry.unset_bit(Bit::Huge);
entry.set_bit(Bit::Global);
assert!(entry.bit(Bit::Global));
entry.unset_bit(Bit::Global);
entry.set_bit(Bit::Noexec);
assert!(entry.bit(Bit::Noexec));
entry.unset_bit(Bit::Noexec);
}