memflow/mem/memory_view/
batcher.rs

1use std::prelude::v1::*;
2
3use super::*;
4use crate::dataview::PodMethods;
5use crate::error::PartialResult;
6use crate::types::Address;
7
8/// A trait for reading data from memory.
9/// mainly auto implemented by the `Batcher` derive macro.
10pub trait Batchable {
11    /// reads all fields batched of the struct from the specified (address + offset) in memory
12    fn read_all_batched(&mut self, view: impl MemoryView, address: Address);
13}
14
15/// This represents the sequence in which memory operations are performed by the batcher when committed to memory.
16pub enum Ordering {
17    ReadWrite,
18    WriteRead,
19}
20
21/// A structure for batching memory reads and writes.
22///
23/// By default, the batcher performs all reads before writes when committing to memory.
24///
25/// # Examples
26///
27/// ```
28/// use memflow::prelude::v1::*;
29/// use memflow::dummy::DummyMemory;
30/// # use memflow::dummy::DummyOs;
31/// # use memflow::architecture::x86::x64;
32///
33/// # let phys_mem = DummyMemory::new(size::mb(16));
34/// # let mut os = DummyOs::new(phys_mem);
35/// # let (dtb, _) = os.alloc_dtb(size::mb(8), &[]);
36/// # let phys_mem = os.into_inner();
37/// # let translator = x64::new_translator(dtb);
38/// let mut virt_mem = VirtualDma::new(phys_mem, x64::ARCH, translator);
39/// let mut batcher = MemoryViewBatcher::new(&mut virt_mem);
40/// ```
41pub struct MemoryViewBatcher<'a, T: MemoryView> {
42    vmem: &'a mut T,
43    read_list: Vec<ReadData<'a>>,
44    write_list: Vec<WriteData<'a>>,
45    ordering: Ordering,
46}
47
48impl<'a, T: MemoryView> MemoryViewBatcher<'a, T> {
49    /// Creates a new `MemoryViewBatcher` instance.
50    ///
51    /// # Examples
52    ///
53    /// ```
54    /// use memflow::prelude::v1::*;
55    /// use memflow::dummy::DummyMemory;
56    /// # use memflow::dummy::DummyOs;
57    /// # use memflow::architecture::x86::x64;
58    ///
59    /// # let phys_mem = DummyMemory::new(size::mb(16));
60    /// # let mut os = DummyOs::new(phys_mem);
61    /// # let (dtb, _) = os.alloc_dtb(size::mb(8), &[]);
62    /// # let phys_mem = os.into_inner();
63    /// # let translator = x64::new_translator(dtb);
64    /// let mut virt_mem = VirtualDma::new(phys_mem, x64::ARCH, translator);
65    /// let mut batcher = MemoryViewBatcher::new(&mut virt_mem);
66    /// ```
67    pub fn new(vmem: &'a mut T) -> Self {
68        Self {
69            vmem,
70            read_list: vec![],
71            write_list: vec![],
72            ordering: Ordering::ReadWrite,
73        }
74    }
75
76    /// Reserves capacity for the read list.
77    /// Reserves capacity for at least `additional` more elements to be handled
78    /// in the given `MemoryViewBatcher<'a, T>`. The internal collection may reserve
79    /// more space to speculatively avoid frequent reallocations.
80    ///
81    /// # Arguments
82    ///
83    /// * `capacity`: The number of operations to reserve space for.
84    ///
85    /// # Examples
86    ///
87    /// ```
88    /// use memflow::prelude::v1::*;
89    /// use memflow::dummy::DummyMemory;
90    /// # use memflow::dummy::DummyOs;
91    /// # use memflow::architecture::x86::x64;
92    ///
93    /// # let phys_mem = DummyMemory::new(size::mb(16));
94    /// # let mut os = DummyOs::new(phys_mem);
95    /// # let (dtb, _) = os.alloc_dtb(size::mb(8), &[]);
96    /// # let phys_mem = os.into_inner();
97    /// # let translator = x64::new_translator(dtb);
98    /// let mut virt_mem = VirtualDma::new(phys_mem, x64::ARCH, translator);
99    /// let mut batcher = MemoryViewBatcher::new(&mut virt_mem);
100    ///
101    /// // Reserve space 10 operations
102    /// batcher.reserve(10);
103    /// ```
104    ///
105    /// # Panics
106    ///
107    /// Panics if the new capacity exceeds `isize::MAX` bytes.
108    pub fn reserve(&mut self, capacity: usize) -> &mut Self {
109        self.read_list.reserve(capacity);
110        self
111    }
112
113    /// Sets the ordering for memory operations performed by the batcher.
114    ///
115    /// You can either perform all reads before writes or vice versa by passing the corresponding `Ordering`.
116    pub fn with_ordering(mut self, ordering: Ordering) -> Self {
117        self.ordering = ordering;
118        self
119    }
120
121    /// Executes all pending operations in this batch.
122    ///
123    /// This also consumes and discards this batcher so it cannot be used anymore.
124    /// The same behavior can be achieved by implicitly calling `drop` on the batcher
125    /// (for example, when going out of scope).
126    ///
127    /// # Examples
128    ///
129    /// ```
130    /// use memflow::prelude::v1::*;
131    /// use memflow::dummy::DummyMemory;
132    /// # use memflow::dummy::DummyOs;
133    /// # use memflow::architecture::x86::x64;
134    ///
135    /// # let phys_mem = DummyMemory::new(size::mb(16));
136    /// # let mut os = DummyOs::new(phys_mem);
137    /// # let (dtb, _) = os.alloc_dtb(size::mb(8), &[]);
138    /// # let phys_mem = os.into_inner();
139    /// # let translator = x64::new_translator(dtb);
140    /// let mut virt_mem = VirtualDma::new(phys_mem, x64::ARCH, translator);
141    /// let mut batcher = MemoryViewBatcher::new(&mut virt_mem);
142    ///
143    /// // commit the batch to memory, this is optional and just used to check if the operations succeed
144    /// batcher.commit_rw().unwrap();
145    /// ```
146    pub fn commit_rw(&mut self) -> PartialResult<()> {
147        match self.ordering {
148            Ordering::ReadWrite => {
149                if !self.read_list.is_empty() {
150                    self.vmem.read_raw_list(&mut self.read_list)?;
151                    self.read_list.clear();
152                }
153
154                if !self.write_list.is_empty() {
155                    self.vmem.write_raw_list(&self.write_list)?;
156                    self.write_list.clear();
157                }
158            }
159            Ordering::WriteRead => {
160                if !self.write_list.is_empty() {
161                    self.vmem.write_raw_list(&self.write_list)?;
162                    self.write_list.clear();
163                }
164
165                if !self.read_list.is_empty() {
166                    self.vmem.read_raw_list(&mut self.read_list)?;
167                    self.read_list.clear();
168                }
169            }
170        }
171
172        Ok(())
173    }
174
175    /// Appends an iterator over read operations `ReadIter` to this batch.
176    ///
177    /// # Arguments
178    ///
179    /// * `iter`: An iterator over `ReadData` instances.
180    ///
181    /// # Examples
182    ///
183    /// ```
184    /// use memflow::prelude::v1::*;
185    /// use memflow::dummy::DummyMemory;
186    /// # use memflow::dummy::DummyOs;
187    /// # use memflow::architecture::x86::x64;
188    ///
189    /// # let phys_mem = DummyMemory::new(size::mb(16));
190    /// # let mut os = DummyOs::new(phys_mem);
191    /// # let (dtb, virt_base) = os.alloc_dtb(size::mb(8), &[]);
192    /// # let phys_mem = os.into_inner();
193    /// # let translator = x64::new_translator(dtb);
194    /// let mut virt_mem = VirtualDma::new(phys_mem, x64::ARCH, translator);
195    ///
196    /// let addr = virt_base; // some arbitrary address
197    /// let mut buf = [0u8; 8];
198    ///
199    /// // create the batcher
200    /// let mut batcher = MemoryViewBatcher::new(&mut virt_mem);
201    ///
202    /// // append the read command
203    /// batcher.read_raw_iter(std::iter::once(CTup2(addr, buf.as_mut().into())).into_iter());
204    ///
205    /// // commit the batch to memory, this is optional and just used to check if the operations succeed
206    /// assert!(batcher.commit_rw().is_ok());
207    /// ```
208    pub fn read_raw_iter(&mut self, iter: impl ReadIterator<'a>) -> &mut Self {
209        self.read_list.extend(iter);
210        self
211    }
212
213    /// Reads data from memory and stores it in the provided buffer.
214    ///
215    /// # Arguments
216    ///
217    /// * `addr`: The starting address to read from.
218    /// * `out`: A mutable reference to the buffer where the data will be stored.
219    ///
220    /// # Example
221    ///
222    /// ```
223    /// use memflow::prelude::v1::*;
224    /// use memflow::dummy::DummyMemory;
225    /// # use memflow::dummy::DummyOs;
226    /// # use memflow::architecture::x86::x64;
227    ///
228    /// # let phys_mem = DummyMemory::new(size::mb(16));
229    /// # let mut os = DummyOs::new(phys_mem);
230    /// # let (dtb, virt_base) = os.alloc_dtb(size::mb(8), &[]);
231    /// # let phys_mem = os.into_inner();
232    /// # let translator = x64::new_translator(dtb);
233    /// let mut virt_mem = VirtualDma::new(phys_mem, x64::ARCH, translator);
234    ///
235    /// let addr = virt_base; // some arbitrary address
236    /// let write_data = [0x10, 0x20, 0x30, 0x40];
237    /// let mut read_data = [0u8; 4];
238    ///
239    /// {
240    ///     // create batcher in a new scope
241    ///     let mut batcher = MemoryViewBatcher::new(&mut virt_mem);
242    ///
243    ///     // write the `write_data` array to memory
244    ///     batcher.write_raw_into(addr, &write_data);
245    ///
246    ///     // commit the batch to memory, this is optional and just used to check if the operations succeed
247    ///     assert!(batcher.commit_rw().is_ok());
248    /// }
249    ///
250    /// // check if the batched write was successful
251    /// virt_mem.read_raw_into(addr, &mut read_data).unwrap();
252    /// assert_eq!(read_data, write_data);
253    /// ```
254    pub fn write_raw_iter(&mut self, iter: impl WriteIterator<'a>) -> &mut Self {
255        self.write_list.extend(iter);
256        self
257    }
258
259    /// Reads data from memory and stores it in the provided buffer.
260    ///
261    /// # Arguments
262    ///
263    /// * `addr` - The address to start reading from.
264    /// * `out` - The buffer to store the read data in.
265    ///
266    /// # Examples
267    ///
268    /// ```
269    /// use memflow::prelude::v1::*;
270    /// use memflow::dummy::DummyMemory;
271    /// # use memflow::dummy::DummyOs;
272    /// # use memflow::architecture::x86::x64;
273    ///
274    /// # let phys_mem = DummyMemory::new(size::mb(16));
275    /// # let mut os = DummyOs::new(phys_mem);
276    /// # let (dtb, virt_base) = os.alloc_dtb(size::mb(8), &[]);
277    /// # let phys_mem = os.into_inner();
278    /// # let translator = x64::new_translator(dtb);
279    /// let mut virt_mem = VirtualDma::new(phys_mem, x64::ARCH, translator);
280    ///
281    /// let addr = virt_base; // some arbitrary address
282    /// let mut buffer = [0u8; 4];
283    ///
284    /// let mut batcher = MemoryViewBatcher::new(&mut virt_mem);
285    ///
286    /// // read 4 bytes from some address and store the result in `buffer`
287    /// batcher.read_raw_into(addr, &mut buffer);
288    ///
289    /// // commit the batch to memory, this is optional and just used to check if the operations succeed
290    /// batcher.commit_rw().unwrap();
291    /// ```
292    pub fn read_raw_into<'b: 'a>(&mut self, addr: Address, out: &'b mut [u8]) -> &mut Self {
293        self.read_raw_iter(std::iter::once(CTup2(addr, out.into())))
294    }
295
296    /// Reads data from memory and stores it in the provided buffer.
297    ///
298    /// # Arguments
299    ///
300    /// * `addr` - The address to read from.
301    /// * `out` - The buffer to store the read data.
302    ///
303    /// # Example
304    ///
305    /// ```
306    /// use memflow::prelude::v1::*;
307    /// use memflow::dummy::DummyMemory;
308    /// # use memflow::dummy::DummyOs;
309    /// # use memflow::architecture::x86::x64;
310    ///
311    /// # let phys_mem = DummyMemory::new(size::mb(16));
312    /// # let mut os = DummyOs::new(phys_mem);
313    /// # let (dtb, virt_base) = os.alloc_dtb(size::mb(8), &[]);
314    /// # let phys_mem = os.into_inner();
315    /// # let translator = x64::new_translator(dtb);
316    /// let mut virt_mem = VirtualDma::new(phys_mem, x64::ARCH, translator);
317    ///
318    /// let addr = virt_base; // some arbitrary address
319    ///
320    /// // writes the text 'hello world' to the specified address in memory
321    /// virt_mem.write(addr, b"hello world").unwrap();
322    ///
323    /// let mut buffer = [0u8; 11];
324    ///
325    /// {
326    ///     // creates a batcher and reads 11 bytes from memory
327    ///     let mut batcher = MemoryViewBatcher::new(&mut virt_mem);
328    ///     batcher.read_into(addr, &mut buffer);
329    ///
330    ///     // commit the batch to memory, this is optional and just used to check if the operations succeed
331    ///     batcher.commit_rw().unwrap();
332    /// }
333    ///
334    /// // compare the memory
335    /// assert_eq!(&buffer, b"hello world");
336    /// ```
337    pub fn read_into<'b: 'a, F: Pod + ?Sized>(
338        &mut self,
339        addr: Address,
340        out: &'b mut F,
341    ) -> &mut Self {
342        self.read_raw_into(addr, out.as_bytes_mut())
343    }
344
345    /// Writes data to memory from the provided buffer.
346    ///
347    /// # Example
348    ///
349    /// ```
350    /// use memflow::prelude::v1::*;
351    /// use memflow::dummy::DummyMemory;
352    /// # use memflow::dummy::DummyOs;
353    /// # use memflow::architecture::x86::x64;
354    ///
355    /// # let phys_mem = DummyMemory::new(size::mb(16));
356    /// # let mut os = DummyOs::new(phys_mem);
357    /// # let (dtb, virt_base) = os.alloc_dtb(size::mb(8), &[]);
358    /// # let phys_mem = os.into_inner();
359    /// # let translator = x64::new_translator(dtb);
360    /// let mut virt_mem = VirtualDma::new(phys_mem, x64::ARCH, translator);
361    ///
362    /// let addr = virt_base; // some arbitrary address
363    /// let write_data = [0x10, 0x20, 0x30, 0x40];
364    /// let mut read_data = [0u8; 4];
365    ///
366    /// {
367    ///     // create batcher in a new scope
368    ///     let mut batcher = MemoryViewBatcher::new(&mut virt_mem);
369    ///
370    ///     // writes the block to memory at the specified address
371    ///     batcher.write_raw_into(addr, &write_data);
372    ///
373    ///     // commit the batch to memory, this is optional and just used to check if the operations succeed
374    ///     assert!(batcher.commit_rw().is_ok());
375    /// }
376    ///
377    /// // check if the write succeeded
378    /// virt_mem.read_raw_into(addr, &mut read_data).unwrap();
379    /// assert_eq!(read_data, write_data);
380    /// ```
381    pub fn write_raw_into<'b: 'a>(&mut self, addr: Address, out: &'b [u8]) -> &mut Self {
382        self.write_raw_iter(std::iter::once(CTup2(addr, out.into())))
383    }
384
385    /// Serializes data and writes it to memory.
386    ///
387    /// # Example
388    ///
389    /// ```
390    /// use memflow::prelude::v1::*;
391    /// use memflow::dummy::DummyMemory;
392    /// # use memflow::dummy::DummyOs;
393    /// # use memflow::architecture::x86::x64;
394    ///
395    /// # let phys_mem = DummyMemory::new(size::mb(16));
396    /// # let mut os = DummyOs::new(phys_mem);
397    /// # let (dtb, virt_base) = os.alloc_dtb(size::mb(8), &[]);
398    /// # let phys_mem = os.into_inner();
399    /// # let translator = x64::new_translator(dtb);
400    /// let mut virt_mem = VirtualDma::new(phys_mem, x64::ARCH, translator);
401    ///
402    /// let addr = virt_base; // some arbitrary address
403    /// let write_data = 0xdeadbeefu64;
404    /// let mut read_data = 0u64;
405    ///
406    /// {
407    ///     // create batcher in a new scope
408    ///     let mut batcher = MemoryViewBatcher::new(&mut virt_mem);
409    ///
410    ///     // writes the block to memory at the specified address
411    ///     batcher.write_into(addr, &write_data);
412    ///
413    ///     // commit the batch to memory, this is optional and just used to check if the operations succeed
414    ///     assert!(batcher.commit_rw().is_ok());
415    /// }
416    ///
417    /// // check if the write succeeded
418    /// virt_mem.read_into(addr, &mut read_data).unwrap();
419    /// assert_eq!(read_data, write_data);
420    /// ```
421    pub fn write_into<'b: 'a, F: Pod + ?Sized>(&mut self, addr: Address, out: &'b F) -> &mut Self {
422        self.write_raw_into(addr, out.as_bytes())
423    }
424}
425
426impl<'a, T: MemoryView> Drop for MemoryViewBatcher<'a, T> {
427    fn drop(&mut self) {
428        let _ = self.commit_rw();
429    }
430}