use core::fmt;
use axerrno::{ax_err, AxError, AxResult};
use axhal::paging::{MappingFlags, PageTable};
use memory_addr::{is_aligned_4k, pa, MemoryAddr, PhysAddr, VirtAddr, VirtAddrRange};
use crate::paging_err_to_ax_err;
pub struct AddrSpace {
va_range: VirtAddrRange,
pt: PageTable,
}
impl AddrSpace {
pub const fn base(&self) -> VirtAddr {
self.va_range.start
}
pub const fn end(&self) -> VirtAddr {
self.va_range.end
}
pub fn size(&self) -> usize {
self.va_range.size()
}
pub const fn page_table(&self) -> &PageTable {
&self.pt
}
pub const fn page_table_root(&self) -> PhysAddr {
self.pt.root_paddr()
}
pub fn contains_range(&self, start: VirtAddr, size: usize) -> bool {
self.va_range
.contains_range(VirtAddrRange::from_start_size(start, size))
}
pub(crate) fn new_empty(base: VirtAddr, size: usize) -> AxResult<Self> {
Ok(Self {
va_range: VirtAddrRange::from_start_size(base, size),
pt: PageTable::try_new().map_err(|_| AxError::NoMemory)?,
})
}
pub fn map_linear(
&mut self,
start_vaddr: VirtAddr,
start_paddr: PhysAddr,
size: usize,
flags: MappingFlags,
) -> AxResult {
if !self.contains_range(start_vaddr, size) {
return ax_err!(InvalidInput, "address out of range");
}
if !start_vaddr.is_aligned_4k() || !start_paddr.is_aligned_4k() || !is_aligned_4k(size) {
return ax_err!(InvalidInput, "address not aligned");
}
let offset = start_vaddr.as_usize() - start_paddr.as_usize();
self.pt
.map_region(
start_vaddr,
|va| pa!(va.as_usize() - offset),
size,
flags,
false, false, )
.map_err(paging_err_to_ax_err)?
.flush_all();
Ok(())
}
pub fn unmap(&mut self, start: VirtAddr, size: usize) -> AxResult {
if !self.contains_range(start, size) {
return ax_err!(InvalidInput, "address out of range");
}
if !start.is_aligned_4k() || !is_aligned_4k(size) {
return ax_err!(InvalidInput, "address not aligned");
}
self.pt
.unmap_region(start, size, true)
.map_err(paging_err_to_ax_err)?
.ignore();
Ok(())
}
pub fn protect(&mut self, start: VirtAddr, size: usize, flags: MappingFlags) -> AxResult {
if !self.contains_range(start, size) {
return ax_err!(InvalidInput, "address out of range");
}
if !start.is_aligned_4k() || !is_aligned_4k(size) {
return ax_err!(InvalidInput, "address not aligned");
}
self.pt
.protect_region(start, size, flags, true)
.map_err(paging_err_to_ax_err)?
.ignore();
Ok(())
}
}
impl fmt::Debug for AddrSpace {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
f.debug_struct("AddrSpace")
.field("va_range", &self.va_range)
.field("page_table_root", &self.pt.root_paddr())
.finish()
}
}