use crate::backend::MemoryOps;
use crate::error::{Error, Result};
use crate::types::*;
const PMASK: u64 = (!0xFu64 << 8) & 0xFFFFFFFFFu64;
pub struct AddressSpace<'a, B: MemoryOps<PhysAddr>> {
backend: &'a B,
dtb: Dtb,
}
#[derive(Debug, PartialEq, Eq, PartialOrd, Ord)]
pub enum PteLevel {
Pte = 1,
Pde,
Pdpte,
Pml4e,
}
pub const fn sign_extend_48bit(address: u64) -> u64 {
if address & 0x0000_8000_0000_0000 != 0 {
address | 0xffff_0000_0000_0000
} else {
address
}
}
impl<'a, B: MemoryOps<PhysAddr>> AddressSpace<'a, B> {
pub fn new(backend: &'a B, dtb: Dtb) -> Self {
Self { backend, dtb }
}
fn read_entry(&self, table_base: PhysAddr, index: u64) -> Result<PageTableEntry> {
self.backend.read(table_base + 8 * index)
}
pub fn virt_to_phys(&self, vaddr: VirtAddr) -> Result<PhysAddr> {
let pml4e = self.read_entry(self.dtb, vaddr.pml4e_index())?;
if !pml4e.is_present() {
return Err(Error::PTEntryNotPresent(PteLevel::Pml4e));
}
let pdpte = self.read_entry(pml4e.0 & PMASK, vaddr.pdpte_index())?;
if !pdpte.is_present() {
return Err(Error::PTEntryNotPresent(PteLevel::Pdpte));
}
if pdpte.is_large_page() {
return Ok((pdpte.0 & (!0u64 << 42 >> 12)) + (vaddr.0 & !(!0u64 << 30)));
}
let pde = self.read_entry(pdpte.0 & PMASK, vaddr.pde_index())?;
if !pde.is_present() {
return Err(Error::PTEntryNotPresent(PteLevel::Pde));
}
if pde.is_large_page() {
return Ok((pde.0 & PMASK) + (vaddr.0 & !(!0u64 << 21)));
}
let pte = self.read_entry(pde.0 & PMASK, vaddr.pte_index())?;
let paddr = pte.0 & PMASK;
if paddr == 0 {
return Err(Error::PTEntryNotPresent(PteLevel::Pte));
}
Ok(paddr + vaddr.page_offset())
}
}
impl<'a, B: MemoryOps<PhysAddr>> MemoryOps<VirtAddr> for AddressSpace<'a, B> {
fn read_bytes(&self, addr: VirtAddr, buf: &mut [u8]) -> Result<usize> {
let paddr = self.virt_to_phys(addr)?;
self.backend.read_bytes(paddr, buf)
}
fn write_bytes(&self, addr: VirtAddr, buf: &[u8]) -> Result<usize> {
let paddr = self.virt_to_phys(addr)?;
self.backend.write_bytes(paddr, buf)
}
}