memflow/mem/virt_mem/
virtual_dma.rs

1use std::prelude::v1::*;
2
3use crate::architecture::{ArchitectureObj, Endianess};
4use crate::error::{Error, Result, *};
5use crate::mem::memory_view::*;
6use crate::mem::{
7    mem_data::*,
8    virt_translate::{
9        DirectTranslate, VirtualTranslate, VirtualTranslate2, VirtualTranslate3,
10        VirtualTranslation, VirtualTranslationCallback, VirtualTranslationFail,
11        VirtualTranslationFailCallback,
12    },
13    MemoryView, PhysicalMemory, PhysicalMemoryMetadata,
14};
15use crate::types::{umem, Address, PhysicalAddress};
16use cglue::tuple::*;
17
18use bumpalo::{collections::Vec as BumpVec, Bump};
19use cglue::callback::FromExtend;
20
21/// The VirtualDma struct provides a default implementation to access virtual memory
22/// from user provided [`PhysicalMemory`] and [`VirtualTranslate2`] objects.
23///
24/// This struct implements [`MemoryView`] and allows the user to access the virtual memory of a process.
25pub struct VirtualDma<T, V, D> {
26    phys_mem: T,
27    vat: V,
28    proc_arch: ArchitectureObj,
29    translator: D,
30    arena: Bump,
31}
32
33impl<T: PhysicalMemory, D: VirtualTranslate3> VirtualDma<T, DirectTranslate, D> {
34    /// Constructs a `VirtualDma` object from user supplied architectures and DTB.
35    /// It creates a default `VirtualTranslate2` object using the `DirectTranslate` struct.
36    ///
37    /// If you want to use a cache for translating virtual to physical memory
38    /// consider using the `VirtualDma::with_vat()` function and supply your own `VirtualTranslate2` object.
39    ///
40    /// # Examples
41    ///
42    /// Constructing a `VirtualDma` object with a given dtb and using it to read:
43    /// ```
44    /// use memflow::types::Address;
45    /// use memflow::architecture::x86::x64;
46    /// use memflow::mem::{PhysicalMemory, VirtualTranslate2, MemoryView, VirtualDma};
47    /// use memflow::cglue::Fwd;
48    ///
49    /// fn read(phys_mem: Fwd<&mut impl PhysicalMemory>, vat: &mut impl VirtualTranslate2, dtb: Address, read_addr: Address) {
50    ///     let arch = x64::ARCH;
51    ///     let translator = x64::new_translator(dtb);
52    ///
53    ///     let mut virt_mem = VirtualDma::new(phys_mem, arch, translator);
54    ///
55    ///     let mut addr = 0u64;
56    ///     virt_mem.read_into(read_addr, &mut addr).unwrap();
57    ///     println!("addr: {:x}", addr);
58    ///     # assert_eq!(addr, 0x00ff_00ff_00ff_00ff);
59    /// }
60    /// # use memflow::dummy::{DummyMemory, DummyOs};
61    /// # use memflow::types::size;
62    /// # use memflow::mem::DirectTranslate;
63    /// # use memflow::cglue::ForwardMut;
64    /// # let mem = DummyMemory::new(size::mb(4));
65    /// # let (mut os, dtb, virt_base) = DummyOs::new_and_dtb(mem, size::mb(2), &[255, 0, 255, 0, 255, 0, 255, 0]);
66    /// # let mut vat = DirectTranslate::new();
67    /// # read(os.forward_mut(), &mut vat, dtb, virt_base);
68    /// ```
69    pub fn new(phys_mem: T, arch: impl Into<ArchitectureObj>, translator: D) -> Self {
70        Self {
71            phys_mem,
72            vat: DirectTranslate::new(),
73            proc_arch: arch.into(),
74            translator,
75            arena: Bump::new(),
76        }
77    }
78}
79
80impl<T: PhysicalMemory, V: VirtualTranslate2, D: VirtualTranslate3> VirtualDma<T, V, D> {
81    /// This function constructs a `VirtualDma` instance with a user supplied `VirtualTranslate2` object.
82    /// It can be used when working with cached virtual to physical translations such as a Tlb.
83    ///
84    /// # Examples
85    ///
86    /// Constructing a `VirtualDma` object with VAT and using it to read:
87    /// ```
88    /// use memflow::types::Address;
89    /// use memflow::architecture::x86::x64;
90    /// use memflow::mem::{PhysicalMemory, VirtualTranslate2, MemoryView, VirtualDma};
91    /// use memflow::cglue::Fwd;
92    ///
93    /// fn read(phys_mem: Fwd<&mut impl PhysicalMemory>, vat: impl VirtualTranslate2, dtb: Address, read_addr: Address) {
94    ///     let arch = x64::ARCH;
95    ///     let translator = x64::new_translator(dtb);
96    ///
97    ///     let mut virt_mem = VirtualDma::with_vat(phys_mem, arch, translator, vat);
98    ///
99    ///     let mut addr = 0u64;
100    ///     virt_mem.read_into(read_addr, &mut addr).unwrap();
101    ///     println!("addr: {:x}", addr);
102    ///     # assert_eq!(addr, 0x00ff_00ff_00ff_00ff);
103    /// }
104    /// # use memflow::dummy::{DummyMemory, DummyOs};
105    /// # use memflow::types::size;
106    /// # use memflow::mem::DirectTranslate;
107    /// # use memflow::cglue::ForwardMut;
108    /// # let mem = DummyMemory::new(size::mb(4));
109    /// # let (mut os, dtb, virt_base) = DummyOs::new_and_dtb(mem, size::mb(2), &[255, 0, 255, 0, 255, 0, 255, 0]);
110    /// # let mut vat = DirectTranslate::new();
111    /// # read(os.forward_mut(), &mut vat, dtb, virt_base);
112    /// ```
113    pub fn with_vat(phys_mem: T, arch: impl Into<ArchitectureObj>, translator: D, vat: V) -> Self {
114        Self {
115            phys_mem,
116            vat,
117            proc_arch: arch.into(),
118            translator,
119            arena: Bump::new(),
120        }
121    }
122
123    /// Returns the architecture of the system. The system architecture is used for virtual to physical translations.
124    pub fn sys_arch(&self) -> ArchitectureObj {
125        self.translator.arch()
126    }
127
128    /// Returns the architecture of the process for this context. The process architecture is mainly used to determine pointer sizes.
129    pub fn proc_arch(&self) -> ArchitectureObj {
130        self.proc_arch
131    }
132
133    /// Replaces current process architecture with a new one.
134    pub fn set_proc_arch(&mut self, new_arch: ArchitectureObj) -> ArchitectureObj {
135        core::mem::replace(&mut self.proc_arch, new_arch)
136    }
137
138    /// Returns the Directory Table Base of this process..
139    pub fn translator(&self) -> &D {
140        &self.translator
141    }
142
143    /// Replace current translator with a new one.
144    pub fn set_translator(&mut self, new_translator: D) -> D {
145        core::mem::replace(&mut self.translator, new_translator)
146    }
147
148    /// A wrapper around `read_addr64` and `read_addr32` that will use the pointer size of this context's process.
149    /// TODO: do this in virt mem
150    pub fn read_addr(&mut self, addr: Address) -> PartialResult<Address> {
151        match self.proc_arch.bits() {
152            64 => self.read_addr64(addr),
153            32 => self.read_addr32(addr),
154            _ => Err(PartialError::Error(Error(
155                ErrorOrigin::VirtualMemory,
156                ErrorKind::InvalidArchitecture,
157            ))),
158        }
159    }
160
161    /// Consumes this VirtualDma object, returning the underlying memory and vat objects
162    pub fn into_inner(self) -> (T, V) {
163        (self.phys_mem, self.vat)
164    }
165
166    pub fn mem_vat_pair(&mut self) -> (&mut T, &mut V) {
167        (&mut self.phys_mem, &mut self.vat)
168    }
169
170    pub fn phys_mem(&mut self) -> &mut T {
171        &mut self.phys_mem
172    }
173
174    pub fn phys_mem_ref(&self) -> &T {
175        &self.phys_mem
176    }
177
178    pub fn vat(&mut self) -> &mut V {
179        &mut self.vat
180    }
181}
182
183impl<T, V, D> Clone for VirtualDma<T, V, D>
184where
185    T: Clone,
186    V: Clone,
187    D: Clone,
188{
189    fn clone(&self) -> Self {
190        Self {
191            phys_mem: self.phys_mem.clone(),
192            vat: self.vat.clone(),
193            proc_arch: self.proc_arch,
194            translator: self.translator.clone(),
195            arena: Bump::new(),
196        }
197    }
198}
199
200#[allow(clippy::needless_option_as_deref)]
201impl<T: PhysicalMemory, V: VirtualTranslate2, D: VirtualTranslate3> MemoryView
202    for VirtualDma<T, V, D>
203{
204    fn read_raw_iter<'a>(
205        &mut self,
206        MemOps {
207            inp,
208            out,
209            mut out_fail,
210        }: ReadRawMemOps,
211    ) -> Result<()> {
212        self.arena.reset();
213
214        let mut translation = BumpVec::with_capacity_in(inp.size_hint().0, &self.arena);
215        let phys_mem = &mut self.phys_mem;
216
217        self.vat.virt_to_phys_iter(
218            phys_mem,
219            &self.translator,
220            inp,
221            &mut translation.from_extend(),
222            &mut (&mut |(_, CTup3(_, meta, buf)): (_, _)| {
223                opt_call(out_fail.as_deref_mut(), CTup2(meta, buf))
224            })
225                .into(),
226        );
227
228        MemOps::with_raw(translation.into_iter(), out, out_fail, |data| {
229            phys_mem.phys_read_raw_iter(data)
230        })
231    }
232
233    fn write_raw_iter(
234        &mut self,
235        MemOps {
236            inp,
237            out,
238            mut out_fail,
239        }: WriteRawMemOps,
240    ) -> Result<()> {
241        self.arena.reset();
242
243        let mut translation = BumpVec::with_capacity_in(inp.size_hint().0, &self.arena);
244        let phys_mem = &mut self.phys_mem;
245
246        self.vat.virt_to_phys_iter(
247            phys_mem,
248            &self.translator,
249            inp,
250            &mut translation.from_extend(),
251            &mut (&mut |(_, CTup3(_, meta, buf)): (_, _)| {
252                opt_call(out_fail.as_deref_mut(), CTup2(meta, buf))
253            })
254                .into(),
255        );
256
257        MemOps::with_raw(translation.into_iter(), out, out_fail, |data| {
258            phys_mem.phys_write_raw_iter(data)
259        })
260    }
261
262    fn metadata(&self) -> MemoryViewMetadata {
263        let PhysicalMemoryMetadata {
264            max_address,
265            real_size,
266            readonly,
267            ..
268        } = self.phys_mem.metadata();
269
270        MemoryViewMetadata {
271            max_address,
272            real_size,
273            readonly,
274            little_endian: self.proc_arch.endianess() == Endianess::LittleEndian,
275            arch_bits: self.proc_arch.bits(),
276        }
277    }
278}
279
280impl<T: PhysicalMemory, V: VirtualTranslate2, D: VirtualTranslate3> VirtualTranslate
281    for VirtualDma<T, V, D>
282{
283    fn virt_to_phys_list(
284        &mut self,
285        addrs: &[VtopRange],
286        mut out: VirtualTranslationCallback,
287        mut out_fail: VirtualTranslationFailCallback,
288    ) {
289        self.vat.virt_to_phys_iter(
290            &mut self.phys_mem,
291            &self.translator,
292            addrs
293                .iter()
294                .map(|&CTup2(address, size)| CTup3(address, address, size)),
295            &mut (&mut |CTup3(a, b, c): CTup3<PhysicalAddress, Address, umem>| {
296                out.call(VirtualTranslation {
297                    in_virtual: b,
298                    size: c,
299                    out_physical: a,
300                })
301            })
302                .into(),
303            &mut (&mut |(_e, CTup3(from, _, size))| {
304                out_fail.call(VirtualTranslationFail { from, size })
305            })
306                .into(),
307        )
308    }
309}