Skip to main content

vmi_core/
arch.rs

1#![doc = include_str!("../docs/arch.md")]
2
3use std::fmt::Debug;
4
5use crate::{AccessContext, AddressContext, Gfn, MemoryAccess, Pa, Va, VmiCore, VmiError, VmiRead};
6
7/// Defines an interface for CPU architecture-specific operations and constants.
8///
9/// The `Architecture` trait provides generic abstraction for interacting with
10/// different CPU architectures in the context of virtual machine introspection.
11///
12/// This trait encapsulates the key characteristics and operations that vary
13/// across different CPU architectures, allowing for the implementation of
14/// architecture-agnostic tools and libraries.
15pub trait Architecture {
16    /// The size of a memory page in bytes for the given architecture.
17    ///
18    /// # Architecture-specific
19    ///
20    /// - **AMD64**: `0x1000` (4096 bytes)
21    const PAGE_SIZE: u64;
22
23    /// The number of bits to shift when converting between page numbers and
24    /// physical addresses.
25    ///
26    /// # Architecture-specific
27    ///
28    /// - **AMD64**: `12` (2^12 = 4096)
29    const PAGE_SHIFT: u64;
30
31    /// A bitmask used to isolate the page number from a full address.
32    ///
33    /// # Architecture-specific
34    ///
35    /// - **AMD64**: `0xFFFFFFFFFFFFF000`
36    const PAGE_MASK: u64;
37
38    /// The machine code for a breakpoint instruction in the given architecture.
39    ///
40    /// # Architecture-specific
41    ///
42    /// - **AMD64**: `&[0xcc]` (`INT3` instruction)
43    const BREAKPOINT: &'static [u8];
44
45    /// The complete set of CPU registers for the architecture.
46    ///
47    /// This type should include general-purpose registers, and all control and
48    /// special registers.
49    type Registers: Registers<Architecture = Self>;
50
51    /// An enumeration representing the levels of page tables in the
52    /// architecture's paging structure.
53    ///
54    /// # Architecture-specific
55    ///
56    /// - **AMD64**: PML5, PML4, PDPT, PD, PT
57    type PageTableLevel: Debug + Clone + Copy;
58
59    /// Various types of interrupts that can occur in the architecture.
60    type Interrupt: Debug + Clone + Copy;
61
62    /// Represents special-purpose registers in the architecture.
63    ///
64    /// # Architecture-specific
65    ///
66    /// - **AMD64**: May represent control registers like `CR0`, `CR2`, `CR3`,
67    ///   `CR4`
68    type SpecialRegister: Debug + Clone + Copy;
69
70    /// Options for monitoring.
71    type EventMonitor;
72
73    /// Architecture-specific event details.
74    type EventReason: EventReason<Architecture = Self>;
75
76    /// Converts a guest physical address (GPA) to a guest frame number (GFN).
77    ///
78    /// # Architecture-specific
79    ///
80    /// - **AMD64**: `gfn = pa >> 12`
81    fn gfn_from_pa(pa: Pa) -> Gfn;
82
83    /// Converts a guest frame number (GFN) to a guest physical address (GPA).
84    ///
85    /// # Architecture-specific
86    ///
87    /// - **AMD64**: `pa = gfn << 12`
88    fn pa_from_gfn(gfn: Gfn) -> Pa;
89
90    /// Combines a guest frame number (GFN) with the in-page offset of a
91    /// virtual address to form a guest physical address (GPA).
92    ///
93    /// # Architecture-specific
94    ///
95    /// - **AMD64**: `pa = (gfn << 12) + (va & 0xfff)`
96    fn pa_in_gfn(gfn: Gfn, va: Va) -> Pa;
97
98    /// Combines a guest frame number (GFN) with the in-page offset of a
99    /// virtual address for a given page table level to form a guest
100    /// physical address (GPA).
101    fn pa_in_gfn_for(gfn: Gfn, va: Va, level: Self::PageTableLevel) -> Pa;
102
103    /// Extracts the offset within a page from a physical address.
104    ///
105    /// # Architecture-specific
106    ///
107    /// - **AMD64**: `offset = pa & 0xfff`
108    fn pa_offset(pa: Pa) -> u64;
109
110    /// Aligns a virtual address down to the nearest page boundary.
111    ///
112    /// # Architecture-specific
113    ///
114    /// - **AMD64**: `va & ~0xfff`
115    fn va_align_down(va: Va) -> Va;
116
117    /// Aligns a virtual address down to the nearest page boundary for a given
118    /// page table level.
119    fn va_align_down_for(va: Va, level: Self::PageTableLevel) -> Va;
120
121    /// Aligns a virtual address up to the nearest page boundary.
122    ///
123    /// # Architecture-specific
124    ///
125    /// - **AMD64**: `(va + 0xfff) & ~0xfff`
126    fn va_align_up(va: Va) -> Va;
127
128    /// Aligns a virtual address up to the nearest page boundary for a given
129    /// page table level.
130    fn va_align_up_for(va: Va, level: Self::PageTableLevel) -> Va;
131
132    /// Extracts the offset within a page from a virtual address.
133    ///
134    /// # Architecture-specific
135    ///
136    /// - **AMD64**: `offset = va & 0xfff`
137    fn va_offset(va: Va) -> u64;
138
139    /// Calculates the offset within a page for a given virtual address and
140    /// page table level.
141    fn va_offset_for(va: Va, level: Self::PageTableLevel) -> u64;
142
143    /// Calculates the index into the lowest level page table for a given
144    /// virtual address.
145    ///
146    /// # Architecture-specific
147    ///
148    /// - **AMD64**: `index = va & 0x1ff`
149    fn va_index(va: Va) -> u64;
150
151    /// Calculates the index into the specified level of the page table
152    /// hierarchy for a given virtual address.
153    fn va_index_for(va: Va, level: Self::PageTableLevel) -> u64;
154
155    /// Performs a full page table walk to translate a virtual address to a
156    /// physical address.
157    fn translate_address<Driver>(vmi: &VmiCore<Driver>, va: Va, root: Pa) -> Result<Pa, VmiError>
158    where
159        Driver: VmiRead<Architecture = Self>;
160}
161
162/// General-purpose register set for a specific architecture.
163///
164/// Marker trait for the concrete GP register struct exposed by each
165/// architecture.
166///
167/// # Architecture-specific
168///
169/// - **AMD64**: `RAX`, `RBX`, `RCX`, `RDX`, `RSI`, `RDI`, `RSP`, `RBP`,
170///   `R8`-`R15`, `RIP` and `RFLAGS`.
171pub trait GpRegisters: Debug + Default + Clone + Copy {
172    /// The specific CPU architecture implementation.
173    type Architecture: Architecture;
174
175    /// Returns the current value of the instruction pointer.
176    ///
177    /// # Architecture-specific
178    ///
179    /// - **AMD64**: `RIP`
180    fn instruction_pointer(&self) -> u64;
181
182    /// Sets the value of the instruction pointer.
183    ///
184    /// # Architecture-specific
185    ///
186    /// - **AMD64**: `RIP`
187    fn set_instruction_pointer(&mut self, ip: u64);
188
189    /// Returns the current value of the stack pointer.
190    ///
191    /// # Architecture-specific
192    ///
193    /// - **AMD64**: `RSP`
194    fn stack_pointer(&self) -> u64;
195
196    /// Sets the value of the stack pointer.
197    ///
198    /// # Architecture-specific
199    ///
200    /// - **AMD64**: `RSP`
201    fn set_stack_pointer(&mut self, sp: u64);
202
203    /// Returns the current value of the result register.
204    ///
205    /// # Architecture-specific
206    ///
207    /// - **AMD64**: `RAX`
208    fn result(&self) -> u64;
209
210    /// Sets the value of the result register.
211    ///
212    /// # Architecture-specific
213    ///
214    /// - **AMD64**: `RAX`
215    fn set_result(&mut self, result: u64);
216}
217
218/// Complete set of CPU registers for a specific architecture.
219///
220/// Provides methods to access and modify key registers and register sets.
221pub trait Registers: Debug + Default + Clone + Copy {
222    /// The specific CPU architecture implementation.
223    type Architecture: Architecture<Registers = Self>;
224
225    /// General-purpose registers of the architecture.
226    ///
227    /// # Architecture-specific
228    ///
229    /// - **AMD64**: `RAX`, `RBX`, `RCX`, `RDX`, `RSI`, `RDI`, `RSP`, `RBP`,
230    ///   `R8`-`R15`, `RIP` and `RFLAGS`.
231    type GpRegisters: GpRegisters<Architecture = Self::Architecture>;
232
233    /// Returns the current value of the instruction pointer.
234    ///
235    /// # Architecture-specific
236    ///
237    /// - **AMD64**: `RIP`
238    fn instruction_pointer(&self) -> u64;
239
240    /// Sets the value of the instruction pointer.
241    ///
242    /// # Architecture-specific
243    ///
244    /// - **AMD64**: `RIP`
245    fn set_instruction_pointer(&mut self, ip: u64);
246
247    /// Returns the current value of the stack pointer.
248    ///
249    /// # Architecture-specific
250    ///
251    /// - **AMD64**: `RSP`
252    fn stack_pointer(&self) -> u64;
253
254    /// Sets the value of the stack pointer.
255    ///
256    /// # Architecture-specific
257    ///
258    /// - **AMD64**: `RSP`
259    fn set_stack_pointer(&mut self, sp: u64);
260
261    /// Returns the current value of the result register.
262    ///
263    /// # Architecture-specific
264    ///
265    /// - **AMD64**: `RAX`
266    fn result(&self) -> u64;
267
268    /// Sets the value of the result register.
269    ///
270    /// # Architecture-specific
271    ///
272    /// - **AMD64**: `RAX`
273    fn set_result(&mut self, result: u64);
274
275    /// Returns a copy of all general-purpose registers.
276    fn gp_registers(&self) -> Self::GpRegisters;
277
278    /// Sets all general-purpose registers.
279    fn set_gp_registers(&mut self, gp: &Self::GpRegisters);
280
281    /// Returns the native address width (i.e. pointer size) of the architecture
282    /// in bytes.
283    ///
284    /// # Architecture-specific
285    ///
286    /// - **AMD64**: 8 bytes if the `CR4.PAE` bit is set, otherwise 4 bytes
287    fn address_width(&self) -> usize;
288
289    /// Returns the effective address width, which may differ from the native
290    /// width (e.g., in compatibility modes).
291    ///
292    /// # Architecture-specific
293    ///
294    /// - **AMD64**: 8 bytes if the `CS.L` bit is set, otherwise 4 bytes
295    fn effective_address_width(&self) -> usize;
296
297    /// Creates an access context for a given virtual address.
298    fn access_context(&self, va: Va) -> AccessContext;
299
300    /// Creates an address context for a given virtual address.
301    fn address_context(&self, va: Va) -> AddressContext;
302
303    /// Returns the physical address of the root of the current page table
304    /// hierarchy for a given virtual address.
305    ///
306    /// # Architecture-specific
307    ///
308    /// - **AMD64**: `CR3 & 0x0000FFFFFFFFF000`
309    fn translation_root(&self, va: Va) -> Pa;
310
311    /// Sets the physical address of the root of the current page table hierarchy
312    /// for a given virtual address.
313    ///
314    /// # Architecture-specific
315    ///
316    /// - **AMD64**: `CR3`
317    fn set_translation_root(&mut self, root: u64, va: Va);
318
319    /// Attempts to determine the return address of the current function call.
320    ///
321    /// # Architecture-specific
322    ///
323    /// - **AMD64**: Value at the top of the stack (i.e. `RSP`)
324    fn return_address<Driver>(&self, vmi: &VmiCore<Driver>) -> Result<Va, VmiError>
325    where
326        Driver: VmiRead<Architecture = Self::Architecture>;
327
328    /// Builds the general-purpose registers that make the current function
329    /// return `value` to its caller without executing its body.
330    ///
331    /// # Architecture-specific
332    ///
333    /// - **AMD64**: Sets `RAX` to `value`, sets `RIP` to the value read from
334    ///   the stack at `RSP`, and advances `RSP` by the effective address width
335    ///   to consume the return address.
336    fn return_from_function<Driver>(
337        &self,
338        vmi: &VmiCore<Driver>,
339        value: u64,
340    ) -> Result<Self::GpRegisters, VmiError>
341    where
342        Driver: VmiRead<Architecture = Self::Architecture>;
343}
344
345/// A memory access event, providing details about the accessed memory.
346pub trait EventMemoryAccess: Debug + Clone + Copy {
347    /// The specific CPU architecture implementation.
348    type Architecture: Architecture;
349
350    /// Returns the physical address of the memory access.
351    fn pa(&self) -> Pa;
352
353    /// Returns the virtual address of the memory access.
354    fn va(&self) -> Va;
355
356    /// Returns the type of memory access (e.g., read, write, execute).
357    fn access(&self) -> MemoryAccess;
358}
359
360/// An interrupt event, providing details about the interrupt.
361pub trait EventInterrupt: Debug + Clone + Copy {
362    /// The specific CPU architecture implementation.
363    type Architecture: Architecture;
364
365    /// Returns the guest frame number where the interrupt occurred.
366    /// Effectively, this is GFN of the current instruction pointer.
367    fn gfn(&self) -> Gfn;
368}
369
370/// The reason for a VM exit or similar event, allowing for type-safe access
371/// to specific event details.
372pub trait EventReason: Debug + Clone + Copy {
373    /// The specific CPU architecture implementation.
374    type Architecture: Architecture<EventReason = Self>;
375
376    /// If the event was caused by a memory access, returns the details
377    /// of that access.
378    fn as_memory_access(
379        &self,
380    ) -> Option<&impl EventMemoryAccess<Architecture = Self::Architecture>>;
381
382    /// If the event was caused by an interrupt, returns the details
383    /// of that interrupt.
384    fn as_interrupt(&self) -> Option<&impl EventInterrupt<Architecture = Self::Architecture>>;
385
386    /// If the event was caused by a software breakpoint, returns the details
387    /// of that breakpoint.
388    fn as_software_breakpoint(
389        &self,
390    ) -> Option<&impl EventInterrupt<Architecture = Self::Architecture>>;
391}