Skip to main content

vmi_core/ctx/
state.rs

1use isr_macros::Field;
2use zerocopy::{FromBytes, Immutable, IntoBytes};
3
4use super::session::VmiSession;
5use crate::{
6    AccessContext, AddressContext, Architecture, Pa, Registers as _, Va, VcpuId, VmiCore, VmiError,
7    VmiRead, VmiWrite,
8    driver::VmiSetRegisters,
9    os::{NoOS, VmiOs},
10};
11
12/// A VMI state.
13///
14/// The state combines access to a [`VmiSession`] with [`Architecture::Registers`]
15/// to provide unified access to VMI operations in the context of a specific
16/// virtual machine state.
17pub struct VmiState<'a, Os>
18where
19    Os: VmiOs,
20{
21    /// The VMI session.
22    session: VmiSession<'a, Os>,
23
24    /// The CPU registers associated with the current VM state.
25    registers: &'a <Os::Architecture as Architecture>::Registers,
26}
27
28impl<Os> Clone for VmiState<'_, Os>
29where
30    Os: VmiOs,
31{
32    fn clone(&self) -> Self {
33        *self
34    }
35}
36
37impl<Os> Copy for VmiState<'_, Os> where Os: VmiOs {}
38
39impl<'a, Os> std::ops::Deref for VmiState<'a, Os>
40where
41    Os: VmiOs,
42{
43    type Target = VmiSession<'a, Os>;
44
45    fn deref(&self) -> &Self::Target {
46        &self.session
47    }
48}
49
50impl<'a, Os> VmiState<'a, Os>
51where
52    Os: VmiOs,
53{
54    /// Creates a new VMI state.
55    pub fn new(
56        session: &'a VmiSession<'a, Os>,
57        registers: &'a <Os::Architecture as Architecture>::Registers,
58    ) -> Self {
59        Self {
60            session: *session,
61            registers,
62        }
63    }
64
65    /// Creates a new VMI state with the specified registers.
66    pub fn with_registers(
67        &'a self,
68        registers: &'a <Os::Architecture as Architecture>::Registers,
69    ) -> Self {
70        Self {
71            session: self.session,
72            registers,
73        }
74    }
75
76    /// Creates a new VMI state without an OS-specific implementation.
77    pub fn without_os(&self) -> VmiState<'a, NoOS<Os::Driver>> {
78        VmiState {
79            session: self.session.without_os(),
80            registers: self.registers,
81        }
82    }
83
84    // Note that `core()` and `underlying_os()` are delegated to the `VmiSession`.
85
86    /// Returns the VMI session.
87    pub fn session(&self) -> &VmiSession<'a, Os> {
88        &self.session
89    }
90
91    /// Returns the CPU registers associated with the current event.
92    pub fn registers(&self) -> &'a <Os::Architecture as Architecture>::Registers {
93        self.registers
94    }
95
96    /// Returns a wrapper providing access to OS-specific operations.
97    pub fn os(&self) -> VmiOsState<'a, Os> {
98        VmiOsState(*self)
99    }
100
101    /// Creates an address context for a given virtual address.
102    pub fn access_context(&self, address: Va) -> AccessContext {
103        self.registers().access_context(address)
104    }
105
106    /// Creates an address context for a given virtual address.
107    pub fn address_context(&self, address: Va) -> AddressContext {
108        self.registers().address_context(address)
109    }
110
111    /// Returns the physical address of the root of the current page table
112    /// hierarchy for a given virtual address.
113    pub fn translation_root(&self, va: Va) -> Pa {
114        self.registers().translation_root(va)
115    }
116}
117
118///////////////////////////////////////////////////////////////////////////////
119// VmiRead
120///////////////////////////////////////////////////////////////////////////////
121
122impl<'a, Os> VmiState<'a, Os>
123where
124    Os: VmiOs,
125    Os::Driver: VmiRead,
126{
127    /// Returns the return address from the current stack frame.
128    pub fn return_address(&self) -> Result<Va, VmiError> {
129        self.registers().return_address(self.core())
130    }
131
132    /// Translates a virtual address to a physical address.
133    pub fn translate_address(&self, va: Va) -> Result<Pa, VmiError> {
134        self.core().translate_address(self.address_context(va))
135    }
136
137    // region: Read
138
139    /// Reads memory from the virtual machine.
140    pub fn read(&self, address: Va, buffer: &mut [u8]) -> Result<(), VmiError> {
141        self.read_in(self.access_context(address), buffer)
142    }
143
144    /// Reads a single byte from the virtual machine.
145    pub fn read_u8(&self, address: Va) -> Result<u8, VmiError> {
146        self.read_u8_in(self.access_context(address))
147    }
148
149    /// Reads a 16-bit unsigned integer from the virtual machine.
150    pub fn read_u16(&self, address: Va) -> Result<u16, VmiError> {
151        self.read_u16_in(self.access_context(address))
152    }
153
154    /// Reads a 32-bit unsigned integer from the virtual machine.
155    pub fn read_u32(&self, address: Va) -> Result<u32, VmiError> {
156        self.read_u32_in(self.access_context(address))
157    }
158
159    /// Reads a 64-bit unsigned integer from the virtual machine.
160    pub fn read_u64(&self, address: Va) -> Result<u64, VmiError> {
161        self.read_u64_in(self.access_context(address))
162    }
163
164    /// Reads an unsigned integer of the specified size from the virtual machine.
165    ///
166    /// This method reads an unsigned integer of the specified size (in bytes)
167    /// from the virtual machine. Note that the size must be 1, 2, 4, or 8.
168    ///
169    /// The result is returned as a [`u64`] to accommodate the widest possible
170    /// integer size.
171    pub fn read_uint(&self, address: Va, size: usize) -> Result<u64, VmiError> {
172        self.read_uint_in(self.access_context(address), size)
173    }
174
175    /// Reads a field of a structure from the virtual machine.
176    ///
177    /// This method reads a field from the virtual machine. The field is
178    /// defined by the provided [`Field`] structure, which specifies the
179    /// offset and size of the field within the memory region.
180    ///
181    /// The result is returned as a [`u64`] to accommodate the widest possible
182    /// integer size.
183    pub fn read_field(&self, base_address: Va, field: &Field) -> Result<u64, VmiError> {
184        self.read_field_in(self.access_context(base_address), field)
185    }
186
187    /// Reads an address-sized unsigned integer from the virtual machine.
188    pub fn read_address(&self, address: Va) -> Result<u64, VmiError> {
189        self.read_address_in(self.access_context(address))
190    }
191
192    /// Reads an address-sized unsigned integer from the virtual machine.
193    pub fn read_address_native(&self, address: Va) -> Result<u64, VmiError> {
194        self.read_address_native_in(self.access_context(address))
195    }
196
197    /// Reads a 32-bit address from the virtual machine.
198    pub fn read_address32(&self, address: Va) -> Result<u64, VmiError> {
199        self.read_address32_in(self.access_context(address))
200    }
201
202    /// Reads a 64-bit address from the virtual machine.
203    pub fn read_address64(&self, address: Va) -> Result<u64, VmiError> {
204        self.read_address64_in(self.access_context(address))
205    }
206
207    /// Reads a virtual address from the virtual machine.
208    pub fn read_va(&self, address: Va) -> Result<Va, VmiError> {
209        self.read_va_in(self.access_context(address))
210    }
211
212    /// Reads a virtual address from the virtual machine.
213    pub fn read_va_native(&self, address: Va) -> Result<Va, VmiError> {
214        self.read_va_native_in(self.access_context(address))
215    }
216
217    /// Reads a 32-bit virtual address from the virtual machine.
218    pub fn read_va32(&self, address: Va) -> Result<Va, VmiError> {
219        self.read_va32_in(self.access_context(address))
220    }
221
222    /// Reads a 64-bit virtual address from the virtual machine.
223    pub fn read_va64(&self, address: Va) -> Result<Va, VmiError> {
224        self.read_va64_in(self.access_context(address))
225    }
226
227    /// Reads a null-terminated string of bytes from the virtual machine with a
228    /// specified limit.
229    pub fn read_string_bytes_limited(
230        &self,
231        address: Va,
232        limit: usize,
233    ) -> Result<Vec<u8>, VmiError> {
234        self.read_string_bytes_limited_in(self.access_context(address), limit)
235    }
236
237    /// Reads a null-terminated string of bytes from the virtual machine.
238    pub fn read_string_bytes(&self, address: Va) -> Result<Vec<u8>, VmiError> {
239        self.read_string_bytes_in(self.access_context(address))
240    }
241
242    /// Reads a null-terminated wide string (UTF-16) from the virtual machine
243    /// with a specified limit.
244    pub fn read_string_utf16_bytes_limited(
245        &self,
246        address: Va,
247        limit: usize,
248    ) -> Result<Vec<u16>, VmiError> {
249        self.read_string_utf16_bytes_limited_in(self.access_context(address), limit)
250    }
251
252    /// Reads a null-terminated wide string (UTF-16) from the virtual machine.
253    pub fn read_string_utf16_bytes(&self, address: Va) -> Result<Vec<u16>, VmiError> {
254        self.read_string_utf16_bytes_in(self.access_context(address))
255    }
256
257    /// Reads a null-terminated string from the virtual machine with a specified
258    /// limit.
259    pub fn read_string_limited(&self, address: Va, limit: usize) -> Result<String, VmiError> {
260        self.read_string_limited_in(self.access_context(address), limit)
261    }
262
263    /// Reads a null-terminated string from the virtual machine.
264    pub fn read_string(&self, address: Va) -> Result<String, VmiError> {
265        self.read_string_in(self.access_context(address))
266    }
267
268    /// Reads a null-terminated wide string (UTF-16) from the virtual machine
269    /// with a specified limit.
270    pub fn read_string_utf16_limited(&self, address: Va, limit: usize) -> Result<String, VmiError> {
271        self.read_string_utf16_limited_in(self.access_context(address), limit)
272    }
273
274    /// Reads a null-terminated wide string (UTF-16) from the virtual machine.
275    pub fn read_string_utf16(&self, address: Va) -> Result<String, VmiError> {
276        self.read_string_utf16_in(self.access_context(address))
277    }
278
279    /// Reads a struct from the virtual machine.
280    pub fn read_struct<T>(&self, address: Va) -> Result<T, VmiError>
281    where
282        T: IntoBytes + FromBytes,
283    {
284        self.read_struct_in(self.access_context(address))
285    }
286
287    // endregion: Read
288
289    // region: Read in
290
291    /// Reads memory from the virtual machine.
292    pub fn read_in(
293        &self,
294        ctx: impl Into<AccessContext>,
295        buffer: &mut [u8],
296    ) -> Result<(), VmiError> {
297        self.core().read(ctx, buffer)
298    }
299
300    /// Reads a single byte from the virtual machine.
301    pub fn read_u8_in(&self, ctx: impl Into<AccessContext>) -> Result<u8, VmiError> {
302        self.core().read_u8(ctx)
303    }
304
305    /// Reads a 16-bit unsigned integer from the virtual machine.
306    pub fn read_u16_in(&self, ctx: impl Into<AccessContext>) -> Result<u16, VmiError> {
307        self.core().read_u16(ctx)
308    }
309
310    /// Reads a 32-bit unsigned integer from the virtual machine.
311    pub fn read_u32_in(&self, ctx: impl Into<AccessContext>) -> Result<u32, VmiError> {
312        self.core().read_u32(ctx)
313    }
314
315    /// Reads a 64-bit unsigned integer from the virtual machine.
316    pub fn read_u64_in(&self, ctx: impl Into<AccessContext>) -> Result<u64, VmiError> {
317        self.core().read_u64(ctx)
318    }
319
320    /// Reads an unsigned integer of the specified size from the virtual machine.
321    ///
322    /// This method reads an unsigned integer of the specified size (in bytes)
323    /// from the virtual machine. Note that the size must be 1, 2, 4, or 8.
324    ///
325    /// The result is returned as a [`u64`] to accommodate the widest possible
326    /// integer size.
327    pub fn read_uint_in(
328        &self,
329        ctx: impl Into<AccessContext>,
330        size: usize,
331    ) -> Result<u64, VmiError> {
332        self.core().read_uint(ctx, size)
333    }
334
335    /// Reads a field of a structure from the virtual machine.
336    ///
337    /// This method reads a field from the virtual machine. The field is
338    /// defined by the provided [`Field`] structure, which specifies the
339    /// offset and size of the field within the memory region.
340    ///
341    /// The result is returned as a [`u64`] to accommodate the widest possible
342    /// integer size.
343    pub fn read_field_in(
344        &self,
345        ctx: impl Into<AccessContext>,
346        field: &Field,
347    ) -> Result<u64, VmiError> {
348        self.core().read_field(ctx, field)
349    }
350
351    /// Reads an address-sized unsigned integer from the virtual machine.
352    pub fn read_address_in(&self, ctx: impl Into<AccessContext>) -> Result<u64, VmiError> {
353        self.core()
354            .read_address(ctx, self.registers().effective_address_width())
355    }
356
357    /// Reads an address-sized unsigned integer from the virtual machine.
358    pub fn read_address_native_in(&self, ctx: impl Into<AccessContext>) -> Result<u64, VmiError> {
359        self.core()
360            .read_address(ctx, self.registers().address_width())
361    }
362
363    /// Reads a 32-bit address from the virtual machine.
364    pub fn read_address32_in(&self, ctx: impl Into<AccessContext>) -> Result<u64, VmiError> {
365        self.core().read_address32(ctx)
366    }
367
368    /// Reads a 64-bit address from the virtual machine.
369    pub fn read_address64_in(&self, ctx: impl Into<AccessContext>) -> Result<u64, VmiError> {
370        self.core().read_address64(ctx)
371    }
372
373    /// Reads a virtual address from the virtual machine.
374    pub fn read_va_in(&self, ctx: impl Into<AccessContext>) -> Result<Va, VmiError> {
375        self.core()
376            .read_va(ctx, self.registers().effective_address_width())
377    }
378
379    /// Reads a virtual address from the virtual machine.
380    pub fn read_va_native_in(&self, ctx: impl Into<AccessContext>) -> Result<Va, VmiError> {
381        self.core().read_va(ctx, self.registers().address_width())
382    }
383
384    /// Reads a 32-bit virtual address from the virtual machine.
385    pub fn read_va32_in(&self, ctx: impl Into<AccessContext>) -> Result<Va, VmiError> {
386        self.core().read_va32(ctx)
387    }
388
389    /// Reads a 64-bit virtual address from the virtual machine.
390    pub fn read_va64_in(&self, ctx: impl Into<AccessContext>) -> Result<Va, VmiError> {
391        self.core().read_va64(ctx)
392    }
393
394    /// Reads a null-terminated string of bytes from the virtual machine with a
395    /// specified limit.
396    pub fn read_string_bytes_limited_in(
397        &self,
398        ctx: impl Into<AccessContext>,
399        limit: usize,
400    ) -> Result<Vec<u8>, VmiError> {
401        self.core().read_string_bytes_limited(ctx, limit)
402    }
403
404    /// Reads a null-terminated string of bytes from the virtual machine.
405    pub fn read_string_bytes_in(&self, ctx: impl Into<AccessContext>) -> Result<Vec<u8>, VmiError> {
406        self.core().read_string_bytes(ctx)
407    }
408
409    /// Reads a null-terminated wide string (UTF-16) from the virtual machine
410    /// with a specified limit.
411    pub fn read_string_utf16_bytes_limited_in(
412        &self,
413        ctx: impl Into<AccessContext>,
414        limit: usize,
415    ) -> Result<Vec<u16>, VmiError> {
416        self.core().read_string_utf16_bytes_limited(ctx, limit)
417    }
418
419    /// Reads a null-terminated wide string (UTF-16) from the virtual machine.
420    pub fn read_string_utf16_bytes_in(
421        &self,
422        ctx: impl Into<AccessContext>,
423    ) -> Result<Vec<u16>, VmiError> {
424        self.core().read_string_utf16_bytes(ctx)
425    }
426
427    /// Reads a null-terminated string from the virtual machine with a specified
428    /// limit.
429    pub fn read_string_limited_in(
430        &self,
431        ctx: impl Into<AccessContext>,
432        limit: usize,
433    ) -> Result<String, VmiError> {
434        self.core().read_string_limited(ctx, limit)
435    }
436
437    /// Reads a null-terminated string from the virtual machine.
438    pub fn read_string_in(&self, ctx: impl Into<AccessContext>) -> Result<String, VmiError> {
439        self.core().read_string(ctx)
440    }
441
442    /// Reads a null-terminated wide string (UTF-16) from the virtual machine
443    /// with a specified limit.
444    pub fn read_string_utf16_limited_in(
445        &self,
446        ctx: impl Into<AccessContext>,
447        limit: usize,
448    ) -> Result<String, VmiError> {
449        self.core().read_string_utf16_limited(ctx, limit)
450    }
451
452    /// Reads a null-terminated wide string (UTF-16) from the virtual machine.
453    pub fn read_string_utf16_in(&self, ctx: impl Into<AccessContext>) -> Result<String, VmiError> {
454        self.core().read_string_utf16(ctx)
455    }
456
457    /// Reads a struct from the virtual machine.
458    pub fn read_struct_in<T>(&self, ctx: impl Into<AccessContext>) -> Result<T, VmiError>
459    where
460        T: IntoBytes + FromBytes,
461    {
462        self.core().read_struct(ctx)
463    }
464
465    // endregion: Read in
466}
467
468///////////////////////////////////////////////////////////////////////////////
469// VmiRead + VmiWrite
470///////////////////////////////////////////////////////////////////////////////
471
472impl<'a, Os> VmiState<'a, Os>
473where
474    Os: VmiOs,
475    Os::Driver: VmiRead + VmiWrite,
476{
477    /// Writes memory to the virtual machine.
478    pub fn write(&self, address: Va, buffer: &[u8]) -> Result<(), VmiError> {
479        self.write_in(self.access_context(address), buffer)
480    }
481
482    /// Writes memory to the virtual machine.
483    pub fn write_in(&self, ctx: impl Into<AccessContext>, buffer: &[u8]) -> Result<(), VmiError> {
484        self.core().write(ctx, buffer)
485    }
486
487    /// Writes a single byte to the virtual machine.
488    pub fn write_u8(&self, address: Va, value: u8) -> Result<(), VmiError> {
489        self.core().write_u8(self.access_context(address), value)
490    }
491
492    /// Writes a 16-bit unsigned integer to the virtual machine.
493    pub fn write_u16(&self, address: Va, value: u16) -> Result<(), VmiError> {
494        self.core().write_u16(self.access_context(address), value)
495    }
496
497    /// Writes a 32-bit unsigned integer to the virtual machine.
498    pub fn write_u32(&self, address: Va, value: u32) -> Result<(), VmiError> {
499        self.core().write_u32(self.access_context(address), value)
500    }
501
502    /// Writes a 64-bit unsigned integer to the virtual machine.
503    pub fn write_u64(&self, address: Va, value: u64) -> Result<(), VmiError> {
504        self.core().write_u64(self.access_context(address), value)
505    }
506
507    /// Writes a struct to the virtual machine.
508    pub fn write_struct<T>(&self, address: Va, value: T) -> Result<(), VmiError>
509    where
510        T: FromBytes + IntoBytes + Immutable,
511    {
512        self.core()
513            .write_struct(self.access_context(address), value)
514    }
515}
516
517///////////////////////////////////////////////////////////////////////////////
518// VmiSetRegisters
519///////////////////////////////////////////////////////////////////////////////
520
521impl<'a, Os> VmiState<'a, Os>
522where
523    Os: VmiOs,
524    Os::Driver: VmiSetRegisters,
525{
526    /// Sets the registers of a virtual CPU.
527    pub fn set_registers(
528        &self,
529        vcpu: VcpuId,
530        registers: <Os::Architecture as Architecture>::Registers,
531    ) -> Result<(), VmiError> {
532        self.core().set_registers(vcpu, registers)
533    }
534}
535
536/// Wrapper providing access to OS-specific operations.
537pub struct VmiOsState<'a, Os>(VmiState<'a, Os>)
538where
539    Os: VmiOs;
540
541impl<'a, Os> VmiOsState<'a, Os>
542where
543    Os: VmiOs,
544{
545    /// Returns the VMI core.
546    pub fn core(&self) -> &'a VmiCore<Os::Driver> {
547        self.0.core()
548    }
549
550    /// Returns the underlying OS-specific implementation.
551    pub fn underlying_os(&self) -> &'a Os {
552        self.0.underlying_os()
553    }
554
555    /// Returns the VMI session.
556    pub fn session(&self) -> &VmiSession<'a, Os> {
557        self.0.session()
558    }
559
560    /// Returns the VMI state.
561    pub fn state(&self) -> VmiState<'a, Os> {
562        self.0
563    }
564
565    /// Returns the CPU registers associated with the current event.
566    pub fn registers(&self) -> &<Os::Architecture as Architecture>::Registers {
567        self.0.registers()
568    }
569
570    /// Retrieves a specific function argument according to the calling
571    /// convention of the operating system.
572    pub fn function_argument_for_registers(
573        &self,
574        registers: &<Os::Architecture as Architecture>::Registers,
575        index: u64,
576    ) -> Result<u64, VmiError> {
577        Os::function_argument(self.0.with_registers(registers), index)
578    }
579
580    /// Retrieves the return value of a function.
581    pub fn function_return_value_for_registers(
582        &self,
583        registers: &<Os::Architecture as Architecture>::Registers,
584    ) -> Result<u64, VmiError> {
585        Os::function_return_value(self.0.with_registers(registers))
586    }
587}