From 0791b01f38ca6eb7927cb2c08964d10a58a98fd0 Mon Sep 17 00:00:00 2001 From: Matthew Leach Date: Wed, 13 May 2026 16:19:52 +0100 Subject: [PATCH] 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. --- .../src/arch/arm64/memory/pg_descriptors.rs | 4 + .../src/arch/x86_64/memory/pg_descriptors.rs | 8 ++ libkernel/src/arch/x86_64/memory/pg_walk.rs | 76 +++++++++++++++++-- libkernel/src/memory/paging/mod.rs | 3 + libkernel/src/memory/paging/walk.rs | 43 ++++++++++- 5 files changed, 126 insertions(+), 8 deletions(-) diff --git a/libkernel/src/arch/arm64/memory/pg_descriptors.rs b/libkernel/src/arch/arm64/memory/pg_descriptors.rs index 0ef95da..55270e4 100644 --- a/libkernel/src/arch/arm64/memory/pg_descriptors.rs +++ b/libkernel/src/arch/arm64/memory/pg_descriptors.rs @@ -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 { + self.permissions() + } } } )? diff --git a/libkernel/src/arch/x86_64/memory/pg_descriptors.rs b/libkernel/src/arch/x86_64/memory/pg_descriptors.rs index 2035819..659df8b 100644 --- a/libkernel/src/arch/x86_64/memory/pg_descriptors.rs +++ b/libkernel/src/arch/x86_64/memory/pg_descriptors.rs @@ -195,6 +195,14 @@ macro_rules! impl_pa_mapper { Some(self.address()) } + + fn permissions(self) -> Option { + if (self.0 & $marker) != $marker { + return None; + } + + Some(self.permissions()) + } } } )+ diff --git a/libkernel/src/arch/x86_64/memory/pg_walk.rs b/libkernel/src/arch/x86_64/memory/pg_walk.rs index 1d63a3d..f28598f 100644 --- a/libkernel/src/arch/x86_64/memory/pg_walk.rs +++ b/libkernel/src/arch/x86_64/memory/pg_walk.rs @@ -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 for PTable { @@ -112,6 +114,70 @@ pub fn get_pte( Ok(descriptor) } +impl Translator for PML4Table { + fn translate( + table_pa: TPA>, + va: VA, + ctx: &mut WalkContext, + ) -> Result> { + 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( + table_pa: TPA>, + va: VA, + ctx: &mut WalkContext, + ) -> Result> { + 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( + pml4_table: TPA>, + va: VA, + mapper: &mut PM, +) -> Result> { + 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::*; diff --git a/libkernel/src/memory/paging/mod.rs b/libkernel/src/memory/paging/mod.rs index feab2bd..0e4093e 100644 --- a/libkernel/src/memory/paging/mod.rs +++ b/libkernel/src/memory/paging/mod.rs @@ -70,6 +70,9 @@ pub trait PaMapper: PageTableEntry { /// Return the mapped physical address. fn mapped_address(self) -> Option; + + /// Return the permissions set on the PTE. + fn permissions(self) -> Option; } /// Trait representing a single level of the page table hierarchy. diff --git a/libkernel/src/memory/paging/walk.rs b/libkernel/src/memory/paging/walk.rs index 39c1f1a..34b8853 100644 --- a/libkernel/src/memory/paging/walk.rs +++ b/libkernel/src/memory/paging/walk.rs @@ -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( + table_pa: TPA>, + va: VA, + ctx: &mut WalkContext, + ) -> crate::error::Result>; +} + +impl Translator for T +where + T: TableMapperTable, + T::Descriptor: PaMapper, + ::NextLevel: Translator, +{ + fn translate( + table_pa: TPA>, + va: VA, + ctx: &mut WalkContext, + ) -> crate::error::Result> { + 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() { + ::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) + } + } +}