use crate::{
FrameAllocator, PageTableEntry, PagingError, PagingResult, PhysAddr, PteConfigOf, 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.as_usize()))
.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 {
Self::from_paddr(pte.paddr(level > 1), 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.as_usize() >> shift) & mask
}
pub(crate) fn level_for_page_size(page_size: usize) -> Option<usize> {
(1..=Self::PT_LEVEL).find(|level| Self::level_size(*level) == page_size)
}
pub fn protect_recursive(
&mut self,
vaddr: VirtAddr,
config: PteConfigOf<T>,
level: usize,
) -> PagingResult<usize> {
let index = Self::virt_to_index(vaddr, level);
let entry = self.as_slice()[index];
if entry.unused() {
return Err(PagingError::not_mapped());
}
let is_dir = level > 1;
let is_huge = entry.huge(is_dir);
if is_huge || level == 1 {
self.as_slice_mut()[index] = T::P::new_page(entry.paddr(is_dir), config, is_huge);
return Ok(Self::level_size(level));
}
if !entry.present() {
return Err(PagingError::not_mapped());
}
let mut child = Self::from_paddr(entry.paddr(is_dir), self.allocator.clone());
child.protect_recursive(vaddr, config, level - 1)
}
pub fn remap_recursive(
&mut self,
vaddr: VirtAddr,
paddr: PhysAddr,
config: PteConfigOf<T>,
level: usize,
) -> PagingResult<usize> {
let index = Self::virt_to_index(vaddr, level);
let entry = self.as_slice()[index];
if entry.unused() {
return Err(PagingError::not_mapped());
}
let is_dir = level > 1;
let is_huge = entry.huge(is_dir);
if is_huge || level == 1 {
let page_size = Self::level_size(level);
let aligned_paddr = PhysAddr::from_usize(paddr.as_usize() & !(page_size - 1));
self.as_slice_mut()[index] = T::P::new_page(aligned_paddr, config, is_huge);
return Ok(page_size);
}
if !entry.present() {
return Err(PagingError::not_mapped());
}
let mut child = Self::from_paddr(entry.paddr(is_dir), self.allocator.clone());
child.remap_recursive(vaddr, paddr, config, level - 1)
}
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 entry = entries[i];
(
entry.present(),
entry.huge(level > 1),
entry.paddr(level > 1),
)
} else {
(false, false, crate::PhysAddr::from_usize(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();
entries_mut[i].clear();
}
}
}
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 (pte, level) = self.find_occupied_leaf(vaddr, level)?;
if !pte.present() {
return Err(PagingError::not_mapped());
}
Ok((pte, level))
}
pub(crate) fn find_occupied_leaf(
&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];
if pte.unused() {
return Err(PagingError::not_mapped());
}
if pte.huge(level > 1) || level == 1 {
return Ok((pte, level));
}
if level > 1 {
if !pte.present() {
return Err(PagingError::hierarchy_error(
"Non-present intermediate entry is not a leaf",
));
}
let child_frame: Frame<T, A> = Frame::from_pte(&pte, level, self.allocator.clone());
return child_frame.find_occupied_leaf(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];
if entry.present() && !entry.huge(true) {
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();
entries_mut[index].clear();
true
} else {
false
}
}
pub(crate) fn clone_entry_from(
&mut self,
source: &Self,
index: usize,
level: usize,
) -> PagingResult<bool> {
if index >= self.len() || index >= source.len() {
return Err(PagingError::hierarchy_error(
"Entry index exceeds page-table frame size",
));
}
if !self.as_slice()[index].unused() {
return Ok(false);
}
let source_entry = source.as_slice()[index];
if source_entry.unused() {
return Ok(false);
}
if level == 1 || source_entry.huge(true) {
self.as_slice_mut()[index] = source_entry;
return Ok(true);
}
if !source_entry.present() {
return Err(PagingError::hierarchy_error(
"Non-present intermediate entry is not a leaf",
));
}
let source_child = Self::from_paddr(source_entry.paddr(true), source.allocator.clone());
let mut target_child = Self::new(self.allocator.clone())?;
if let Err(err) = target_child.clone_children_from(&source_child, level - 1) {
target_child.deallocate_recursive(level - 1);
return Err(err);
}
self.as_slice_mut()[index] = T::P::new_table(target_child.paddr);
Ok(true)
}
fn clone_children_from(&mut self, source: &Self, level: usize) -> PagingResult {
for index in 0..source.len() {
self.clone_entry_from(source, index, level)?;
}
Ok(())
}
}
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
}