memflow 0.2.0-beta9

core components of the memflow physical memory introspection framework
Documentation
use std::prelude::v1::*;

use crate::architecture::{ArchitectureObj, Endianess};
use crate::error::{Error, Result, *};
use crate::mem::memory_view::*;
use crate::mem::{
    mem_data::*,
    virt_translate::{
        DirectTranslate, VirtualTranslate, VirtualTranslate2, VirtualTranslate3,
        VirtualTranslation, VirtualTranslationCallback, VirtualTranslationFail,
        VirtualTranslationFailCallback,
    },
    MemoryView, PhysicalMemory, PhysicalMemoryMetadata,
};
use crate::types::{umem, Address, PhysicalAddress};
use cglue::tuple::*;

use bumpalo::{collections::Vec as BumpVec, Bump};
use cglue::callback::FromExtend;

/// The VirtualDma struct provides a default implementation to access virtual memory
/// from user provided [`PhysicalMemory`] and [`VirtualTranslate2`] objects.
///
/// This struct implements [`MemoryView`] and allows the user to access the virtual memory of a process.
pub struct VirtualDma<T, V, D> {
    phys_mem: T,
    vat: V,
    proc_arch: ArchitectureObj,
    translator: D,
    arena: Bump,
}

impl<T: PhysicalMemory, D: VirtualTranslate3> VirtualDma<T, DirectTranslate, D> {
    /// Constructs a `VirtualDma` object from user supplied architectures and DTB.
    /// It creates a default `VirtualTranslate2` object using the `DirectTranslate` struct.
    ///
    /// If you want to use a cache for translating virtual to physical memory
    /// consider using the `VirtualDma::with_vat()` function and supply your own `VirtualTranslate2` object.
    ///
    /// # Examples
    ///
    /// Constructing a `VirtualDma` object with a given dtb and using it to read:
    /// ```
    /// use memflow::types::Address;
    /// use memflow::architecture::x86::x64;
    /// use memflow::mem::{PhysicalMemory, VirtualTranslate2, MemoryView, VirtualDma};
    /// use memflow::cglue::Fwd;
    ///
    /// fn read(phys_mem: Fwd<&mut impl PhysicalMemory>, vat: &mut impl VirtualTranslate2, dtb: Address, read_addr: Address) {
    ///     let arch = x64::ARCH;
    ///     let translator = x64::new_translator(dtb);
    ///
    ///     let mut virt_mem = VirtualDma::new(phys_mem, arch, translator);
    ///
    ///     let mut addr = 0u64;
    ///     virt_mem.read_into(read_addr, &mut addr).unwrap();
    ///     println!("addr: {:x}", addr);
    ///     # assert_eq!(addr, 0x00ff_00ff_00ff_00ff);
    /// }
    /// # use memflow::dummy::{DummyMemory, DummyOs};
    /// # use memflow::types::size;
    /// # use memflow::mem::DirectTranslate;
    /// # use memflow::cglue::ForwardMut;
    /// # let mem = DummyMemory::new(size::mb(4));
    /// # let (mut os, dtb, virt_base) = DummyOs::new_and_dtb(mem, size::mb(2), &[255, 0, 255, 0, 255, 0, 255, 0]);
    /// # let mut vat = DirectTranslate::new();
    /// # read(os.forward_mut(), &mut vat, dtb, virt_base);
    /// ```
    pub fn new(phys_mem: T, arch: impl Into<ArchitectureObj>, translator: D) -> Self {
        Self {
            phys_mem,
            vat: DirectTranslate::new(),
            proc_arch: arch.into(),
            translator,
            arena: Bump::new(),
        }
    }
}

impl<T: PhysicalMemory, V: VirtualTranslate2, D: VirtualTranslate3> VirtualDma<T, V, D> {
    /// This function constructs a `VirtualDma` instance with a user supplied `VirtualTranslate2` object.
    /// It can be used when working with cached virtual to physical translations such as a Tlb.
    ///
    /// # Examples
    ///
    /// Constructing a `VirtualDma` object with VAT and using it to read:
    /// ```
    /// use memflow::types::Address;
    /// use memflow::architecture::x86::x64;
    /// use memflow::mem::{PhysicalMemory, VirtualTranslate2, MemoryView, VirtualDma};
    /// use memflow::cglue::Fwd;
    ///
    /// fn read(phys_mem: Fwd<&mut impl PhysicalMemory>, vat: impl VirtualTranslate2, dtb: Address, read_addr: Address) {
    ///     let arch = x64::ARCH;
    ///     let translator = x64::new_translator(dtb);
    ///
    ///     let mut virt_mem = VirtualDma::with_vat(phys_mem, arch, translator, vat);
    ///
    ///     let mut addr = 0u64;
    ///     virt_mem.read_into(read_addr, &mut addr).unwrap();
    ///     println!("addr: {:x}", addr);
    ///     # assert_eq!(addr, 0x00ff_00ff_00ff_00ff);
    /// }
    /// # use memflow::dummy::{DummyMemory, DummyOs};
    /// # use memflow::types::size;
    /// # use memflow::mem::DirectTranslate;
    /// # use memflow::cglue::ForwardMut;
    /// # let mem = DummyMemory::new(size::mb(4));
    /// # let (mut os, dtb, virt_base) = DummyOs::new_and_dtb(mem, size::mb(2), &[255, 0, 255, 0, 255, 0, 255, 0]);
    /// # let mut vat = DirectTranslate::new();
    /// # read(os.forward_mut(), &mut vat, dtb, virt_base);
    /// ```
    pub fn with_vat(phys_mem: T, arch: impl Into<ArchitectureObj>, translator: D, vat: V) -> Self {
        Self {
            phys_mem,
            vat,
            proc_arch: arch.into(),
            translator,
            arena: Bump::new(),
        }
    }

    /// Returns the architecture of the system. The system architecture is used for virtual to physical translations.
    pub fn sys_arch(&self) -> ArchitectureObj {
        self.translator.arch()
    }

    /// Returns the architecture of the process for this context. The process architecture is mainly used to determine pointer sizes.
    pub fn proc_arch(&self) -> ArchitectureObj {
        self.proc_arch
    }

    /// Replaces current process architecture with a new one.
    pub fn set_proc_arch(&mut self, new_arch: ArchitectureObj) -> ArchitectureObj {
        core::mem::replace(&mut self.proc_arch, new_arch)
    }

    /// Returns the Directory Table Base of this process..
    pub fn translator(&self) -> &D {
        &self.translator
    }

    /// Replace current translator with a new one.
    pub fn set_translator(&mut self, new_translator: D) -> D {
        core::mem::replace(&mut self.translator, new_translator)
    }

    /// A wrapper around `read_addr64` and `read_addr32` that will use the pointer size of this context's process.
    /// TODO: do this in virt mem
    pub fn read_addr(&mut self, addr: Address) -> PartialResult<Address> {
        match self.proc_arch.bits() {
            64 => self.read_addr64(addr),
            32 => self.read_addr32(addr),
            _ => Err(PartialError::Error(Error(
                ErrorOrigin::VirtualMemory,
                ErrorKind::InvalidArchitecture,
            ))),
        }
    }

    /// Consumes this VirtualDma object, returning the underlying memory and vat objects
    pub fn into_inner(self) -> (T, V) {
        (self.phys_mem, self.vat)
    }

    pub fn mem_vat_pair(&mut self) -> (&mut T, &mut V) {
        (&mut self.phys_mem, &mut self.vat)
    }

    pub fn phys_mem(&mut self) -> &mut T {
        &mut self.phys_mem
    }

    pub fn phys_mem_ref(&self) -> &T {
        &self.phys_mem
    }

    pub fn vat(&mut self) -> &mut V {
        &mut self.vat
    }
}

impl<T, V, D> Clone for VirtualDma<T, V, D>
where
    T: Clone,
    V: Clone,
    D: Clone,
{
    fn clone(&self) -> Self {
        Self {
            phys_mem: self.phys_mem.clone(),
            vat: self.vat.clone(),
            proc_arch: self.proc_arch,
            translator: self.translator.clone(),
            arena: Bump::new(),
        }
    }
}

#[allow(clippy::needless_option_as_deref)]
impl<T: PhysicalMemory, V: VirtualTranslate2, D: VirtualTranslate3> MemoryView
    for VirtualDma<T, V, D>
{
    fn read_raw_iter<'a>(
        &mut self,
        MemOps {
            inp,
            out,
            mut out_fail,
        }: ReadRawMemOps,
    ) -> Result<()> {
        self.arena.reset();

        let mut translation = BumpVec::with_capacity_in(inp.size_hint().0, &self.arena);
        let phys_mem = &mut self.phys_mem;

        self.vat.virt_to_phys_iter(
            phys_mem,
            &self.translator,
            inp,
            &mut translation.from_extend(),
            &mut (&mut |(_, CTup3(_, meta, buf)): (_, _)| {
                opt_call(out_fail.as_deref_mut(), CTup2(meta, buf))
            })
                .into(),
        );

        MemOps::with_raw(translation.into_iter(), out, out_fail, |data| {
            phys_mem.phys_read_raw_iter(data)
        })
    }

    fn write_raw_iter(
        &mut self,
        MemOps {
            inp,
            out,
            mut out_fail,
        }: WriteRawMemOps,
    ) -> Result<()> {
        self.arena.reset();

        let mut translation = BumpVec::with_capacity_in(inp.size_hint().0, &self.arena);
        let phys_mem = &mut self.phys_mem;

        self.vat.virt_to_phys_iter(
            phys_mem,
            &self.translator,
            inp,
            &mut translation.from_extend(),
            &mut (&mut |(_, CTup3(_, meta, buf)): (_, _)| {
                opt_call(out_fail.as_deref_mut(), CTup2(meta, buf))
            })
                .into(),
        );

        MemOps::with_raw(translation.into_iter(), out, out_fail, |data| {
            phys_mem.phys_write_raw_iter(data)
        })
    }

    fn metadata(&self) -> MemoryViewMetadata {
        let PhysicalMemoryMetadata {
            max_address,
            real_size,
            readonly,
            ..
        } = self.phys_mem.metadata();

        MemoryViewMetadata {
            max_address,
            real_size,
            readonly,
            little_endian: self.proc_arch.endianess() == Endianess::LittleEndian,
            arch_bits: self.proc_arch.bits(),
        }
    }
}

impl<T: PhysicalMemory, V: VirtualTranslate2, D: VirtualTranslate3> VirtualTranslate
    for VirtualDma<T, V, D>
{
    fn virt_to_phys_list(
        &mut self,
        addrs: &[VtopRange],
        mut out: VirtualTranslationCallback,
        mut out_fail: VirtualTranslationFailCallback,
    ) {
        self.vat.virt_to_phys_iter(
            &mut self.phys_mem,
            &self.translator,
            addrs
                .iter()
                .map(|&CTup2(address, size)| CTup3(address, address, size)),
            &mut (&mut |CTup3(a, b, c): CTup3<PhysicalAddress, Address, umem>| {
                out.call(VirtualTranslation {
                    in_virtual: b,
                    size: c,
                    out_physical: a,
                })
            })
                .into(),
            &mut (&mut |(_e, CTup3(from, _, size))| {
                out_fail.call(VirtualTranslationFail { from, size })
            })
                .into(),
        )
    }
}