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}