use alloc::{sync::Arc, vec::Vec};
use core::ops::Deref;
use ax_errno::AxResult;
use ax_hal::paging::{MappingFlags, PageSize, PageTableCursor};
use ax_memory_addr::{MemoryAddr, PhysAddr, VirtAddr, VirtAddrRange};
use ax_sync::Mutex;
use super::{AddrSpace, Backend, BackendOps, alloc_frame, dealloc_frame, divide_page, pages_in};
pub struct SharedPages {
pub phys_pages: Vec<PhysAddr>,
pub size: PageSize,
}
impl SharedPages {
pub fn new(size: usize, page_size: PageSize) -> AxResult<Self> {
let num_pages = divide_page(size, page_size);
let mut result = Self {
phys_pages: Vec::with_capacity(num_pages),
size: page_size,
};
for _ in 0..num_pages {
result.phys_pages.push(alloc_frame(true, page_size)?);
}
Ok(result)
}
pub fn len(&self) -> usize {
self.phys_pages.len()
}
pub fn is_empty(&self) -> bool {
self.phys_pages.is_empty()
}
}
impl Deref for SharedPages {
type Target = [PhysAddr];
fn deref(&self) -> &Self::Target {
&self.phys_pages
}
}
impl Drop for SharedPages {
fn drop(&mut self) {
for frame in &self.phys_pages {
dealloc_frame(*frame, self.size);
}
}
}
#[derive(Clone)]
pub struct SharedBackend {
start: VirtAddr,
pages: Arc<SharedPages>,
}
impl SharedBackend {
pub fn pages(&self) -> &Arc<SharedPages> {
&self.pages
}
fn pages_starting_from(&self, start: VirtAddr) -> &[PhysAddr] {
debug_assert!(start.is_aligned(self.pages.size));
let start_index = divide_page(start - self.start, self.pages.size);
&self.pages[start_index..]
}
}
impl BackendOps for SharedBackend {
fn page_size(&self) -> PageSize {
self.pages.size
}
fn map(&self, range: VirtAddrRange, flags: MappingFlags, pt: &mut PageTableCursor) -> AxResult {
debug!("Shared::map: {:?} {:?}", range, flags);
for (vaddr, paddr) in
pages_in(range, self.pages.size)?.zip(self.pages_starting_from(range.start))
{
pt.map(vaddr, *paddr, self.pages.size, flags)?;
}
Ok(())
}
fn unmap(&self, range: VirtAddrRange, pt: &mut PageTableCursor) -> AxResult {
debug!("Shared::unmap: {:?}", range);
for vaddr in pages_in(range, self.pages.size)? {
pt.unmap(vaddr)?;
}
Ok(())
}
fn clone_map(
&self,
_range: VirtAddrRange,
_flags: MappingFlags,
_old_pt: &mut PageTableCursor,
_new_pt: &mut PageTableCursor,
_new_aspace: &Arc<Mutex<AddrSpace>>,
) -> AxResult<Backend> {
Ok(Backend::Shared(self.clone()))
}
}
impl Backend {
pub fn new_shared(start: VirtAddr, pages: Arc<SharedPages>) -> Self {
Self::Shared(SharedBackend { start, pages })
}
}