use crate::{
FrameAllocator, PageTableEntry, PagingError, PagingResult, PhysAddr, PteConfig, TableMeta,
VirtAddr,
};
#[derive(Clone, Copy)]
pub struct Frame<T: TableMeta, A: FrameAllocator> {
pub paddr: PhysAddr,
pub allocator: A,
frames: usize,
_marker: core::marker::PhantomData<T>,
}
impl<T: TableMeta, A: FrameAllocator> core::fmt::Debug for Frame<T, A> {
fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
f.debug_struct("Frame")
.field("paddr", &format_args!("{:#x}", self.paddr.raw()))
.finish()
}
}
impl<T, A> Frame<T, A>
where
T: TableMeta,
A: FrameAllocator,
{
pub(crate) const PT_INDEX_SHIFT: usize = T::PAGE_SIZE.trailing_zeros() as usize;
pub(crate) const PT_INDEX_BITS: usize = cal_index_bits::<T>();
pub(crate) const PT_VALID_BITS: usize = Self::PT_INDEX_BITS + Self::PT_INDEX_SHIFT;
pub(crate) const LEN: usize = T::PAGE_SIZE / core::mem::size_of::<T::P>();
pub(crate) const ROOT_LEN: usize = 1usize << T::LEVEL_BITS[0];
pub(crate) const ROOT_FRAMES: usize =
(Self::ROOT_LEN * core::mem::size_of::<T::P>()).div_ceil(T::PAGE_SIZE);
pub(crate) const PT_LEVEL: usize = T::LEVEL_BITS.len();
pub fn new(allocator: A) -> PagingResult<Self> {
let paddr = allocator.alloc_frame().ok_or(PagingError::NoMemory)?;
unsafe {
let vaddr = allocator.phys_to_virt(paddr);
core::ptr::write_bytes(vaddr, 0, T::PAGE_SIZE);
}
Ok(Self {
paddr,
allocator,
frames: 1,
_marker: core::marker::PhantomData,
})
}
pub fn new_root(allocator: A) -> PagingResult<Self> {
let align = T::PAGE_SIZE * Self::ROOT_FRAMES;
let paddr = allocator
.alloc_frames(Self::ROOT_FRAMES, align)
.ok_or(PagingError::NoMemory)?;
unsafe {
let vaddr = allocator.phys_to_virt(paddr);
core::ptr::write_bytes(vaddr, 0, T::PAGE_SIZE * Self::ROOT_FRAMES);
}
Ok(Self {
paddr,
allocator,
frames: Self::ROOT_FRAMES,
_marker: core::marker::PhantomData,
})
}
pub fn from_paddr(paddr: PhysAddr, allocator: A) -> Self {
Self {
paddr,
allocator,
frames: 1,
_marker: core::marker::PhantomData,
}
}
pub fn from_root_paddr(paddr: PhysAddr, allocator: A) -> Self {
Self {
paddr,
allocator,
frames: Self::ROOT_FRAMES,
_marker: core::marker::PhantomData,
}
}
pub fn from_pte(pte: &T::P, level: usize, allocator: A) -> Self {
let config = pte.to_config(level > 1);
Self::from_paddr(config.paddr, allocator)
}
pub fn as_slice_mut(&mut self) -> &mut [T::P] {
let vaddr = self.allocator.phys_to_virt(self.paddr);
unsafe { core::slice::from_raw_parts_mut(vaddr as *mut T::P, self.len()) }
}
pub fn as_slice(&self) -> &[T::P] {
let vaddr = self.allocator.phys_to_virt(self.paddr);
unsafe { core::slice::from_raw_parts(vaddr as *const T::P, self.len()) }
}
pub fn len(&self) -> usize {
self.frames * Self::LEN
}
pub fn is_empty(&self) -> bool {
self.len() == 0
}
pub fn level_size(level: usize) -> usize {
if level == 1 {
return T::PAGE_SIZE;
}
let total_levels = T::LEVEL_BITS.len();
let shift = T::LEVEL_BITS
.iter()
.skip(total_levels - level + 1)
.sum::<usize>();
T::PAGE_SIZE << shift
}
pub fn virt_to_index(vaddr: VirtAddr, level: usize) -> usize {
if level == 0 || level > Self::PT_LEVEL {
panic!("Invalid level: {} (valid: 1..={})", level, Self::PT_LEVEL);
}
let page_shift = T::PAGE_SIZE.trailing_zeros() as usize;
let total_levels = T::LEVEL_BITS.len();
let shift = if level == 1 {
page_shift
} else {
page_shift
+ T::LEVEL_BITS
.iter()
.skip(total_levels - level + 1)
.sum::<usize>()
};
let level_index_bits = T::LEVEL_BITS[total_levels - level];
let mask = (1 << level_index_bits) - 1;
(vaddr.raw() >> shift) & mask
}
pub fn reconstruct_vaddr(index: usize, level: usize, base_vaddr: VirtAddr) -> VirtAddr {
let entry_size = Self::level_size(level);
base_vaddr + index * entry_size
}
pub fn deallocate_recursive(&mut self, level: usize) {
self.deallocate_children(level);
self.allocator
.dealloc_frames(self.paddr, self.frames, T::PAGE_SIZE);
}
pub fn deallocate_children(&mut self, level: usize) {
for i in (0..self.len()).rev() {
let entry_info = {
let entries = self.as_slice();
if i < entries.len() {
let config = entries[i].to_config(level > 1);
(config.valid, config.huge, config.paddr)
} else {
(false, false, crate::PhysAddr::new(0))
}
};
let (is_valid, is_huge, paddr) = entry_info;
if !is_valid {
continue;
}
if is_huge || level == 1 {
continue;
}
else {
let mut child_frame = Frame::<T, A>::from_paddr(paddr, self.allocator.clone());
child_frame.deallocate_recursive(level - 1);
let entries_mut = self.as_slice_mut();
let invalid_config = PteConfig {
valid: false,
..Default::default()
};
entries_mut[i] = T::P::from_config(invalid_config);
}
}
}
pub fn translate_recursive(&self, vaddr: VirtAddr, level: usize) -> PagingResult<T::P> {
let (pte, _) = self.translate_recursive_with_level(vaddr, level)?;
Ok(pte)
}
pub fn translate_recursive_with_level(
&self,
vaddr: VirtAddr,
level: usize,
) -> PagingResult<(T::P, usize)> {
let index = Self::virt_to_index(vaddr, level);
let entries = self.as_slice();
let pte = entries[index];
let config = pte.to_config(level > 1);
if !config.valid {
return Err(PagingError::not_mapped());
}
if config.huge || level == 1 {
return Ok((pte, level));
}
if level > 1 {
let child_frame: Frame<T, A> = Frame::from_pte(&pte, level, self.allocator.clone());
return child_frame.translate_recursive_with_level(vaddr, level - 1);
}
Err(PagingError::hierarchy_error(
"Invalid page table level during translation",
))
}
pub fn dealloc_entry_recursive(&mut self, index: usize, level: usize) -> bool {
if index >= self.len() || level <= 1 {
return false;
}
let entries = self.as_slice();
let entry = &entries[index];
let config = entry.to_config(level > 1);
if config.valid && !config.huge {
let mut child_frame = Frame::<T, A>::from_pte(entry, level, self.allocator.clone());
child_frame.deallocate_recursive(level - 1);
let entries_mut = self.as_slice_mut();
let invalid_config = PteConfig {
valid: false,
..Default::default()
};
entries_mut[index] = T::P::from_config(invalid_config);
true
} else {
false
}
}
}
const fn cal_index_bits<T: TableMeta>() -> usize {
let mut bits = 0;
let len = T::LEVEL_BITS.len();
let mut i = 0;
while i < len {
bits += T::LEVEL_BITS[i];
i += 1;
}
bits
}