vmi_core/
arch.rs

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
#![doc = include_str!("../docs/arch.md")]

use std::fmt::Debug;

use crate::{AddressContext, Gfn, MemoryAccess, Pa, Va, VmiCore, VmiDriver, VmiError};

/// Defines an interface for CPU architecture-specific operations and constants.
///
/// The `Architecture` trait provides generic abstraction for interacting with
/// different CPU architectures in the context of virtual machine introspection.
///
/// This trait encapsulates the key characteristics and operations that vary
/// across different CPU architectures, allowing for the implementation of
/// architecture-agnostic tools and libraries.
pub trait Architecture {
    /// The size of a memory page in bytes for the given architecture.
    ///
    /// # Architecture-specific
    ///
    /// - **AMD64**: `0x1000` (4096 bytes)
    const PAGE_SIZE: u64;

    /// The number of bits to shift when converting between page numbers and
    /// physical addresses.
    ///
    /// # Architecture-specific
    ///
    /// - **AMD64**: `12` (2^12 = 4096)
    const PAGE_SHIFT: u64;

    /// A bitmask used to isolate the page number from a full address.
    ///
    /// # Architecture-specific
    ///
    /// - **AMD64**: `0xFFFFFFFFFFFFF000`
    const PAGE_MASK: u64;

    /// The machine code for a breakpoint instruction in the given architecture.
    ///
    /// # Architecture-specific
    ///
    /// - **AMD64**: `&[0xcc]` (`INT3` instruction)
    const BREAKPOINT: &'static [u8];

    /// The complete set of CPU registers for the architecture.
    ///
    /// This type should include general-purpose registers, and all control and
    /// special registers.
    type Registers: Registers;

    /// An enumeration representing the levels of page tables in the
    /// architecture's paging structure.
    ///
    /// # Architecture-specific
    ///
    /// - **AMD64**: PML5, PML4, PDPT, PD, PT
    type PageTableLevel: Debug + Clone + Copy;

    /// Various types of interrupts that can occur in the architecture.
    type Interrupt: Debug + Clone + Copy;

    /// Represents special-purpose registers in the architecture.
    ///
    /// # Architecture-specific
    ///
    /// - **AMD64**: May represent control registers like `CR0`, `CR2`, `CR3`,
    ///   `CR4`
    type SpecialRegister: Debug + Clone + Copy;

    /// Options for monitoring.
    type EventMonitor;

    /// Architecture-specific event details.
    type EventReason: EventReason;

    /// Converts a guest physical address (GPA) to a guest frame number (GFN).
    ///
    /// # Architecture-specific
    ///
    /// - **AMD64**: `gfn = pa >> 12`
    fn gfn_from_pa(pa: Pa) -> Gfn;

    /// Converts a guest frame number (GFN) to a guest physical address (GPA).
    ///
    /// # Architecture-specific
    ///
    /// - **AMD64**: `pa = gfn << 12`
    fn pa_from_gfn(gfn: Gfn) -> Pa;

    /// Extracts the offset within a page from a physical address.
    ///
    /// # Architecture-specific
    ///
    /// - **AMD64**: `offset = pa & 0xfff`
    fn pa_offset(pa: Pa) -> u64;

    /// Extracts the offset within a page from a virtual address.
    ///
    /// # Architecture-specific
    ///
    /// - **AMD64**: `offset = va & 0xfff`
    fn va_offset(va: Va) -> u64;

    /// Calculates the offset within a page for a given virtual address and
    /// page table level.
    fn va_offset_for(va: Va, level: Self::PageTableLevel) -> u64;

    /// Calculates the index into the lowest level page table for a given
    /// virtual address.
    ///
    /// # Architecture-specific
    ///
    /// - **AMD64**: `index = va & 0x1ff`
    fn va_index(va: Va) -> u64;

    /// Calculates the index into the specified level of the page table
    /// hierarchy for a given virtual address.
    fn va_index_for(va: Va, level: Self::PageTableLevel) -> u64;

    /// Performs a full page table walk to translate a virtual address to a
    /// physical address.
    fn translate_address<Driver>(vmi: &VmiCore<Driver>, va: Va, root: Pa) -> Result<Pa, VmiError>
    where
        Driver: VmiDriver<Architecture = Self>;
}

/// Complete set of CPU registers for a specific architecture.
///
/// Provides methods to access and modify key registers and register sets.
pub trait Registers
where
    Self: Debug + Default + Clone + Copy,
{
    /// The specific CPU architecture implementation.
    type Architecture: Architecture + ?Sized;

    /// General-purpose registers of the architecture.
    ///
    /// # Architecture-specific
    ///
    /// - **AMD64**: `RAX`, `RBX`, `RCX`, `RDX`, `RSI`, `RDI`, `RSP`, `RBP`,
    ///   `R8`-`R15`, `RIP` and `RFLAGS`.
    type GpRegisters: Debug + Default + Clone + Copy;

    /// Returns the current value of the instruction pointer.
    ///
    /// # Architecture-specific
    ///
    /// - **AMD64**: `RIP`
    fn instruction_pointer(&self) -> u64;

    /// Sets the value of the instruction pointer.
    ///
    /// # Architecture-specific
    ///
    /// - **AMD64**: `RIP`
    fn set_instruction_pointer(&mut self, ip: u64);

    /// Returns the current value of the stack pointer.
    ///
    /// # Architecture-specific
    ///
    /// - **AMD64**: `RSP`
    fn stack_pointer(&self) -> u64;

    /// Sets the value of the stack pointer.
    ///
    /// # Architecture-specific
    ///
    /// - **AMD64**: `RSP`
    fn set_stack_pointer(&mut self, sp: u64);

    /// Returns the current value of the result register.
    ///
    /// # Architecture-specific
    ///
    /// - **AMD64**: `RAX`
    fn result(&self) -> u64;

    /// Sets the value of the result register.
    ///
    /// # Architecture-specific
    ///
    /// - **AMD64**: `RAX`
    fn set_result(&mut self, result: u64);

    /// Returns a copy of all general-purpose registers.
    fn gp_registers(&self) -> Self::GpRegisters;

    /// Sets all general-purpose registers.
    fn set_gp_registers(&mut self, gp: &Self::GpRegisters);

    /// Returns the native address width (i.e. pointer size) of the architecture
    /// in bytes.
    ///
    /// # Architecture-specific
    ///
    /// - **AMD64**: 8 bytes
    fn address_width(&self) -> usize;

    /// Returns the effective address width, which may differ from the native
    /// width (e.g., in compatibility modes).
    ///
    /// # Architecture-specific
    ///
    /// - **AMD64**: 8 bytes if the `CS.L` bit is set, otherwise 4 bytes
    fn effective_address_width(&self) -> usize;

    /// Creates an address context for a given virtual address.
    fn address_context(&self, va: Va) -> AddressContext;

    /// Returns the physical address of the root of the current page table
    /// hierarchy for a given virtual address.
    ///
    /// # Architecture-specific
    ///
    /// - **AMD64**: `CR3 & 0x0000FFFFFFFFF000`
    fn translation_root(&self, va: Va) -> Pa;

    /// Attempts to determine the return address of the current function call.
    ///
    /// # Architecture-specific
    ///
    /// - **AMD64**: Value at the top of the stack (i.e. `RSP`)
    fn return_address<Driver>(&self, vmi: &VmiCore<Driver>) -> Result<Va, VmiError>
    where
        Driver: VmiDriver;
}

/// A memory access event, providing details about the accessed memory.
pub trait EventMemoryAccess
where
    Self: Debug + Clone + Copy,
{
    /// The specific CPU architecture implementation.
    type Architecture: Architecture + ?Sized;

    /// Returns the physical address of the memory access.
    fn pa(&self) -> Pa;

    /// Returns the virtual address of the memory access.
    fn va(&self) -> Va;

    /// Returns the type of memory access (e.g., read, write, execute).
    fn access(&self) -> MemoryAccess;
}

/// An interrupt event, providing details about the interrupt.
pub trait EventInterrupt
where
    Self: Debug + Clone + Copy,
{
    /// The specific CPU architecture implementation.
    type Architecture: Architecture + ?Sized;

    /// Returns the guest frame number where the interrupt occurred.
    /// Effectively, this is GFN of the current instruction pointer.
    fn gfn(&self) -> Gfn;
}

/// The reason for a VM exit or similar event, allowing for type-safe access
/// to specific event details.
pub trait EventReason
where
    Self: Debug + Clone + Copy,
{
    /// The specific CPU architecture implementation.
    type Architecture: Architecture + ?Sized;

    /// If the event was caused by a memory access, returns the details
    /// of that access.
    fn as_memory_access(
        &self,
    ) -> Option<&impl EventMemoryAccess<Architecture = Self::Architecture>>;

    /// If the event was caused by an interrupt, returns the details
    /// of that interrupt.
    fn as_interrupt(&self) -> Option<&impl EventInterrupt<Architecture = Self::Architecture>>;

    /// If the event was caused by a software breakpoint, returns the details
    /// of that breakpoint.
    fn as_software_breakpoint(
        &self,
    ) -> Option<&impl EventInterrupt<Architecture = Self::Architecture>>;
}