ntoseye 0.5.0

Windows kernel debugger for Linux hosts running Windows under KVM/QEMU
use crate::backend::MemoryOps;
use crate::error::{Error, Result};
use crate::types::*;

// PageFrameNumber
const PMASK: u64 = (!0xFu64 << 8) & 0xFFFFFFFFFu64;

// 'a = lifetime of the borrow of the backend
//  B = any type that implements phys mem
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)
    }

    // TODO lots of bad casting here, needs to be rewritten
    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)
    }
}