mirror of
https://github.com/hexagonal-sun/moss-kernel.git
synced 2026-05-24 08:55:20 -04:00
libkernel: paging: add translate
Add a `translate` function which is much more powerful that `get_pte`. It permits block mappings to be returned for a given VA.
This commit is contained in:
@@ -219,6 +219,10 @@ macro_rules! define_descriptor {
|
||||
let addr = reg.read(BlockPageFields::OUTPUT_ADDR);
|
||||
Some(PA::from_value((addr << Self::MAP_SHIFT) as usize))
|
||||
}
|
||||
|
||||
fn permissions(self) -> Option<PtePermissions> {
|
||||
self.permissions()
|
||||
}
|
||||
}
|
||||
}
|
||||
)?
|
||||
|
||||
@@ -195,6 +195,14 @@ macro_rules! impl_pa_mapper {
|
||||
|
||||
Some(self.address())
|
||||
}
|
||||
|
||||
fn permissions(self) -> Option<PtePermissions> {
|
||||
if (self.0 & $marker) != $marker {
|
||||
return None;
|
||||
}
|
||||
|
||||
Some(self.permissions())
|
||||
}
|
||||
}
|
||||
}
|
||||
)+
|
||||
|
||||
@@ -4,18 +4,20 @@ use crate::{
|
||||
error::{MapError, Result},
|
||||
memory::{
|
||||
PAGE_SIZE,
|
||||
address::{TPA, VA},
|
||||
address::{PA, TPA, VA},
|
||||
paging::{
|
||||
NullTlbInvalidator, PageTableEntry, PageTableMapper, PgTable, PgTableArray,
|
||||
walk::{RecursiveWalker, WalkContext},
|
||||
NullTlbInvalidator, PaMapper, PageTableEntry, PageTableMapper, PgTable, PgTableArray,
|
||||
TableMapper,
|
||||
permissions::PtePermissions,
|
||||
walk::{RecursiveWalker, Translator, WalkContext},
|
||||
},
|
||||
region::VirtMemoryRegion,
|
||||
region::{PhysMemoryRegion, VirtMemoryRegion},
|
||||
},
|
||||
};
|
||||
|
||||
use super::{
|
||||
pg_descriptors::PTE,
|
||||
pg_tables::{PML4Table, PTable},
|
||||
pg_tables::{PDPTable, PML4Table, PTable},
|
||||
};
|
||||
|
||||
impl RecursiveWalker<PTE> for PTable {
|
||||
@@ -112,6 +114,70 @@ pub fn get_pte<PM: PageTableMapper>(
|
||||
Ok(descriptor)
|
||||
}
|
||||
|
||||
impl Translator for PML4Table {
|
||||
fn translate<PM: PageTableMapper>(
|
||||
table_pa: TPA<PgTableArray<Self>>,
|
||||
va: VA,
|
||||
ctx: &mut WalkContext<PM>,
|
||||
) -> Result<Option<(PA, usize, PtePermissions)>> {
|
||||
let desc = unsafe {
|
||||
ctx.mapper
|
||||
.with_page_table(table_pa, |pgtable| Self::from_ptr(pgtable).get_desc(va))?
|
||||
};
|
||||
match desc.next_table_address() {
|
||||
Some(next_pa) => PDPTable::translate(next_pa, va, ctx),
|
||||
None if desc.is_valid() => Err(MapError::InvalidDescriptor.into()),
|
||||
None => Ok(None),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl Translator for PTable {
|
||||
fn translate<PM: PageTableMapper>(
|
||||
table_pa: TPA<PgTableArray<Self>>,
|
||||
va: VA,
|
||||
ctx: &mut WalkContext<PM>,
|
||||
) -> Result<Option<(PA, usize, PtePermissions)>> {
|
||||
let desc = unsafe {
|
||||
ctx.mapper
|
||||
.with_page_table(table_pa, |pgtable| Self::from_ptr(pgtable).get_desc(va))?
|
||||
};
|
||||
|
||||
match desc.mapped_address() {
|
||||
Some(pa) => Ok(Some((
|
||||
pa,
|
||||
1 << Self::Descriptor::MAP_SHIFT,
|
||||
desc.permissions(),
|
||||
))),
|
||||
None if desc.is_valid() => Err(MapError::InvalidDescriptor.into()),
|
||||
None => Ok(None),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// Translates the VA into a physical region plus an offset and permissions.
|
||||
pub fn translate<PM: PageTableMapper>(
|
||||
pml4_table: TPA<PgTableArray<PML4Table>>,
|
||||
va: VA,
|
||||
mapper: &mut PM,
|
||||
) -> Result<Option<(PhysMemoryRegion, usize, PtePermissions)>> {
|
||||
let mut walk_ctx = WalkContext {
|
||||
mapper,
|
||||
// Safe to not invalidate the TLB, as we are not modifying any PTEs.
|
||||
invalidator: &NullTlbInvalidator {},
|
||||
};
|
||||
|
||||
if let Some((pa, blk_sz, perms)) = PML4Table::translate(pml4_table, va, &mut walk_ctx)? {
|
||||
debug_assert!(blk_sz.is_power_of_two());
|
||||
|
||||
let offset = va.value() & (blk_sz - 1);
|
||||
|
||||
Ok(Some((PhysMemoryRegion::new(pa, blk_sz), offset, perms)))
|
||||
} else {
|
||||
Ok(None)
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use super::*;
|
||||
|
||||
@@ -70,6 +70,9 @@ pub trait PaMapper: PageTableEntry {
|
||||
|
||||
/// Return the mapped physical address.
|
||||
fn mapped_address(self) -> Option<PA>;
|
||||
|
||||
/// Return the permissions set on the PTE.
|
||||
fn permissions(self) -> Option<PtePermissions>;
|
||||
}
|
||||
|
||||
/// Trait representing a single level of the page table hierarchy.
|
||||
|
||||
@@ -3,14 +3,14 @@
|
||||
use crate::{
|
||||
error::MapError,
|
||||
memory::{
|
||||
address::{TPA, VA},
|
||||
address::{PA, TPA, VA},
|
||||
region::VirtMemoryRegion,
|
||||
},
|
||||
};
|
||||
|
||||
use super::{
|
||||
PageTableEntry, PageTableMapper, PgTable, PgTableArray, TLBInvalidator, TableMapper,
|
||||
TableMapperTable,
|
||||
PaMapper, PageTableEntry, PageTableMapper, PgTable, PgTableArray, TLBInvalidator, TableMapper,
|
||||
TableMapperTable, permissions::PtePermissions,
|
||||
};
|
||||
|
||||
/// A collection of context required to modify page tables.
|
||||
@@ -96,3 +96,40 @@ where
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
pub(crate) trait Translator: PgTable + Sized {
|
||||
fn translate<PM: PageTableMapper>(
|
||||
table_pa: TPA<PgTableArray<Self>>,
|
||||
va: VA,
|
||||
ctx: &mut WalkContext<PM>,
|
||||
) -> crate::error::Result<Option<(PA, usize, PtePermissions)>>;
|
||||
}
|
||||
|
||||
impl<T> Translator for T
|
||||
where
|
||||
T: TableMapperTable,
|
||||
T::Descriptor: PaMapper,
|
||||
<T::Descriptor as TableMapper>::NextLevel: Translator,
|
||||
{
|
||||
fn translate<PM: PageTableMapper>(
|
||||
table_pa: TPA<PgTableArray<Self>>,
|
||||
va: VA,
|
||||
ctx: &mut WalkContext<PM>,
|
||||
) -> crate::error::Result<Option<(PA, usize, PtePermissions)>> {
|
||||
let desc = unsafe {
|
||||
ctx.mapper
|
||||
.with_page_table(table_pa, |pgtable| T::from_ptr(pgtable).get_desc(va))?
|
||||
};
|
||||
|
||||
if let Some(next_pa) = desc.next_table_address() {
|
||||
<T::Descriptor as TableMapper>::NextLevel::translate(next_pa, va, ctx)
|
||||
} else if let Some(block_pa) = desc.mapped_address() {
|
||||
let block_size = 1usize << T::Descriptor::MAP_SHIFT;
|
||||
Ok(Some((block_pa, block_size, desc.permissions().unwrap())))
|
||||
} else if desc.is_valid() {
|
||||
Err(MapError::InvalidDescriptor)?
|
||||
} else {
|
||||
Ok(None)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user