Files
RedBear-OS/src/page/table.rs
T
2020-09-06 21:06:58 -06:00

109 lines
3.0 KiB
Rust

use core::marker::PhantomData;
use crate::{
Arch,
PhysicalAddress,
VirtualAddress,
};
use super::PageEntry;
pub struct PageTable<A> {
base: VirtualAddress,
phys: PhysicalAddress,
level: usize,
phantom: PhantomData<A>,
}
impl<A: Arch> PageTable<A> {
pub unsafe fn new(base: VirtualAddress, phys: PhysicalAddress, level: usize) -> Self {
Self { base, phys, level, phantom: PhantomData }
}
pub unsafe fn top() -> Self {
Self::new(
VirtualAddress::new(0),
A::table(),
A::PAGE_LEVELS - 1
)
}
pub fn base(&self) -> VirtualAddress {
self.base
}
pub fn phys(&self) -> PhysicalAddress {
self.phys
}
pub fn level(&self) -> usize {
self.level
}
pub unsafe fn virt(&self) -> VirtualAddress {
A::phys_to_virt(self.phys)
// Recursive mapping
// let mut addr = 0xFFFF_FFFF_FFFF_F000;
// for level in (self.level + 1 .. A::PAGE_LEVELS).rev() {
// let index = (self.base.0 >> (level * A::PAGE_ENTRY_SHIFT + A::PAGE_SHIFT)) & A::PAGE_ENTRY_MASK;
// addr <<= A::PAGE_ENTRY_SHIFT;
// addr |= index << A::PAGE_SHIFT;
// }
// VirtualAddress::new(addr)
}
pub fn entry_base(&self, i: usize) -> Option<VirtualAddress> {
if i < A::PAGE_ENTRIES {
let level_shift = self.level * A::PAGE_ENTRY_SHIFT + A::PAGE_SHIFT;
Some(self.base.add(i << level_shift))
} else {
None
}
}
pub unsafe fn entry_virt(&self, i: usize) -> Option<VirtualAddress> {
if i < A::PAGE_ENTRIES {
Some(self.virt().add(i * A::PAGE_ENTRY_SIZE))
} else {
None
}
}
pub unsafe fn entry(&self, i: usize) -> Option<PageEntry<A>> {
let addr = self.entry_virt(i)?;
Some(PageEntry::new(A::read::<usize>(addr)))
}
pub unsafe fn set_entry(&mut self, i: usize, entry: PageEntry<A>) -> Option<()> {
let addr = self.entry_virt(i)?;
A::write::<usize>(addr, entry.data());
Some(())
}
pub unsafe fn index_of(&self, address: VirtualAddress) -> Option<usize> {
// Canonicalize address first
let address = VirtualAddress::new(address.data() & A::PAGE_ADDRESS_MASK);
let level_shift = self.level * A::PAGE_ENTRY_SHIFT + A::PAGE_SHIFT;
let level_mask = (A::PAGE_ENTRIES << level_shift) - 1;
if address >= self.base && address <= self.base.add(level_mask) {
Some((address.data() >> level_shift) & A::PAGE_ENTRY_MASK)
} else {
None
}
}
pub unsafe fn next(&self, i: usize) -> Option<Self> {
if self.level > 0 {
let entry = self.entry(i)?;
if entry.present() {
return Some(PageTable::new(
self.entry_base(i)?,
entry.address(),
self.level - 1
));
}
}
None
}
}