libkernel: move ClaimedPage into libkernel crate

By moving `ClaimedPage` into libkernel we can utilise it in
arch-agonstic ode.
This commit is contained in:
Matthew Leach
2025-12-16 19:53:39 +00:00
committed by Ashwin Naren
parent c1bea5ecbb
commit 54f2d683b8
5 changed files with 125 additions and 95 deletions

View File

@@ -297,7 +297,7 @@ impl PA {
}
/// Trait for translating between physical and virtual addresses.
pub trait AddressTranslator<T> {
pub trait AddressTranslator<T>: 'static + Send + Sync {
fn virt_to_phys(va: TVA<T>) -> TPA<T>;
fn phys_to_virt(pa: TPA<T>) -> TVA<T>;
}

View File

@@ -1,6 +1,18 @@
use super::{
PAGE_SHIFT, address::AddressTranslator, page_alloc::PageAllocGetter, region::PhysMemoryRegion,
};
use crate::{
CpuOps,
error::Result,
memory::{
PAGE_SIZE,
address::{PA, VA},
page_alloc::PageAllocation,
},
};
use alloc::slice;
use core::fmt::Display;
use super::{PAGE_SHIFT, PAGE_SIZE, address::PA, region::PhysMemoryRegion};
use core::marker::PhantomData;
#[derive(Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Debug)]
pub struct PageFrame {
@@ -36,3 +48,96 @@ impl PageFrame {
}
}
}
/// A conveniance wrapper for dealing with single-page allocaitons.
pub struct ClaimedPage<A: CpuOps, G: PageAllocGetter<A>, T: AddressTranslator<()>>(
PageAllocation<'static, A>,
PhantomData<G>,
PhantomData<T>,
);
impl<A: CpuOps, G: PageAllocGetter<A>, T: AddressTranslator<()>> Display for ClaimedPage<A, G, T> {
fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
write!(f, "{}", self.0.region().start_address().to_pfn())
}
}
impl<A: CpuOps, G: PageAllocGetter<A>, T: AddressTranslator<()>> ClaimedPage<A, G, T> {
/// Allocates a single physical page. The contents of the page are
/// undefined.
fn alloc() -> Result<Self> {
let frame = G::global_page_alloc().get().unwrap().alloc_frames(0)?;
Ok(Self(frame, PhantomData, PhantomData))
}
/// Allocates a single physical page and zeroes its contents.
pub fn alloc_zeroed() -> Result<Self> {
let mut page = Self::alloc()?;
page.as_slice_mut().fill(0);
Ok(page)
}
/// Takes ownership of the page at pfn.
///
/// SAFETY: Ensure that the calling context does indeed own this page.
/// Otherwise, the page may be free'd when it's owned by another context.
pub unsafe fn from_pfn(pfn: PageFrame) -> Self {
Self(
unsafe {
G::global_page_alloc()
.get()
.unwrap()
.alloc_from_region(pfn.as_phys_range())
},
PhantomData,
PhantomData,
)
}
#[inline(always)]
pub fn pa(&self) -> PA {
self.0.region().start_address()
}
/// Returns the kernel virtual address where this page is mapped.
#[inline(always)]
pub fn va(&self) -> VA {
self.pa().to_va::<T>()
}
/// Returns a raw pointer to the page's content.
#[inline(always)]
pub fn as_ptr(&self) -> *const u8 {
self.va().as_ptr() as *const _
}
/// Returns a mutable raw pointer to the page's content.
#[inline(always)]
pub fn as_ptr_mut(&self) -> *mut u8 {
self.va().as_ptr_mut() as *mut _
}
/// Returns a slice representing the page's content.
#[inline(always)]
pub fn as_slice(&self) -> &[u8] {
// This is safe because:
// 1. We have a reference `&self`, guaranteeing safe access.
// 2. The pointer is valid and aligned.
// 3. The lifetime of the slice is tied to `&self` by the compiler.
unsafe { slice::from_raw_parts(self.as_ptr(), PAGE_SIZE) }
}
/// Returns a mutable slice representing the page's content.
#[inline(always)]
pub fn as_slice_mut(&mut self) -> &mut [u8] {
// This is safe because:
// 1. We have a mutable reference `&mut self`, guaranteeing exclusive access.
// 2. The pointer is valid and aligned.
// 3. The lifetime of the slice is tied to `&mut self` by the compiler.
unsafe { slice::from_raw_parts_mut(self.as_ptr_mut(), PAGE_SIZE) }
}
pub fn leak(self) -> PageFrame {
self.0.leak().start_address().to_pfn()
}
}

View File

@@ -2,7 +2,7 @@ use crate::{
CpuOps,
error::{KernelError, Result},
memory::{PAGE_SHIFT, address::AddressTranslator, page::PageFrame, smalloc::Smalloc},
sync::spinlock::SpinLockIrq,
sync::{once_lock::OnceLock, spinlock::SpinLockIrq},
};
use core::{
cmp::min,
@@ -422,6 +422,10 @@ impl<CPU: CpuOps> FrameAllocator<CPU> {
}
}
pub trait PageAllocGetter<C: CpuOps>: Send + Sync + 'static {
fn global_page_alloc() -> &'static OnceLock<FrameAllocator<C>, C>;
}
#[cfg(test)]
mod tests {
use super::*;

View File

@@ -6,6 +6,9 @@ pub struct PageOffsetTranslator<VM: VirtualMemory> {
_phantom: PhantomData<VM>,
}
unsafe impl<VM: VirtualMemory> Send for PageOffsetTranslator<VM> {}
unsafe impl<VM: VirtualMemory> Sync for PageOffsetTranslator<VM> {}
impl<T, VM: VirtualMemory> AddressTranslator<T> for PageOffsetTranslator<VM> {
fn virt_to_phys(va: TVA<T>) -> TPA<T> {
let mut v = va.value();

View File

@@ -1,98 +1,16 @@
use super::{PAGE_ALLOC, PageOffsetTranslator};
use crate::arch::ArchImpl;
use alloc::slice;
use core::fmt::Display;
use libkernel::{
error::Result,
memory::{
PAGE_SIZE,
address::{PA, VA},
page::PageFrame,
page_alloc::PageAllocation,
},
};
use libkernel::memory::page_alloc::PageAllocGetter;
/// An conveniance wrapper for dealing with single-page allocaitons.
pub struct ClaimedPage(PageAllocation<'static, ArchImpl>);
pub struct PgAllocGetter {}
impl Display for ClaimedPage {
fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
write!(f, "{}", self.0.region().start_address().to_pfn())
impl PageAllocGetter<ArchImpl> for PgAllocGetter {
fn global_page_alloc() -> &'static libkernel::sync::once_lock::OnceLock<
libkernel::memory::page_alloc::FrameAllocator<ArchImpl>,
ArchImpl,
> {
&PAGE_ALLOC
}
}
impl ClaimedPage {
/// Allocates a single physical page. The contents of the page are
/// undefined.
fn alloc() -> Result<Self> {
let frame = PAGE_ALLOC.get().unwrap().alloc_frames(0)?;
Ok(Self(frame))
}
/// Allocates a single physical page and zeroes its contents.
pub fn alloc_zeroed() -> Result<Self> {
let mut page = Self::alloc()?;
page.as_slice_mut().fill(0);
Ok(page)
}
/// Takes ownership of the page at pfn.
///
/// SAFETY: Ensure that the calling context does indeed own this page.
/// Otherwise, the page may be free'd when it's owned by another context.
pub unsafe fn from_pfn(pfn: PageFrame) -> Self {
Self(unsafe {
PAGE_ALLOC
.get()
.unwrap()
.alloc_from_region(pfn.as_phys_range())
})
}
#[inline(always)]
pub fn pa(&self) -> PA {
self.0.region().start_address()
}
/// Returns the kernel virtual address where this page is mapped.
#[inline(always)]
pub fn va(&self) -> VA {
self.pa().to_va::<PageOffsetTranslator>()
}
/// Returns a raw pointer to the page's content.
#[inline(always)]
pub fn as_ptr(&self) -> *const u8 {
self.va().as_ptr() as *const _
}
/// Returns a mutable raw pointer to the page's content.
#[inline(always)]
pub fn as_ptr_mut(&self) -> *mut u8 {
self.va().as_ptr_mut() as *mut _
}
/// Returns a slice representing the page's content.
#[inline(always)]
pub fn as_slice(&self) -> &[u8] {
// This is safe because:
// 1. We have a reference `&self`, guaranteeing safe access.
// 2. The pointer is valid and aligned.
// 3. The lifetime of the slice is tied to `&self` by the compiler.
unsafe { slice::from_raw_parts(self.as_ptr(), PAGE_SIZE) }
}
/// Returns a mutable slice representing the page's content.
#[inline(always)]
pub fn as_slice_mut(&mut self) -> &mut [u8] {
// This is safe because:
// 1. We have a mutable reference `&mut self`, guaranteeing exclusive access.
// 2. The pointer is valid and aligned.
// 3. The lifetime of the slice is tied to `&mut self` by the compiler.
unsafe { slice::from_raw_parts_mut(self.as_ptr_mut(), PAGE_SIZE) }
}
pub fn leak(self) -> PageFrame {
self.0.leak().start_address().to_pfn()
}
}
pub type ClaimedPage = libkernel::memory::page::ClaimedPage<ArchImpl, PgAllocGetter, PageOffsetTranslator>;