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, 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    /// Builds the general-purpose registers that make the current function
133    /// return `value` to its caller without executing its body.
134    pub fn return_from_function(
135        &self,
136        value: u64,
137    ) -> Result<<<Os::Architecture as Architecture>::Registers as Registers>::GpRegisters, VmiError>
138    {
139        self.registers().return_from_function(self.core(), value)
140    }
141
142    /// Translates a virtual address to a physical address.
143    pub fn translate_address(&self, va: Va) -> Result<Pa, VmiError> {
144        self.core().translate_address(self.address_context(va))
145    }
146
147    // region: Read
148
149    /// Reads memory from the virtual machine.
150    pub fn read(&self, address: Va, buffer: &mut [u8]) -> Result<(), VmiError> {
151        self.read_in(self.access_context(address), buffer)
152    }
153
154    /// Reads a single byte from the virtual machine.
155    pub fn read_u8(&self, address: Va) -> Result<u8, VmiError> {
156        self.read_u8_in(self.access_context(address))
157    }
158
159    /// Reads a 16-bit unsigned integer from the virtual machine.
160    pub fn read_u16(&self, address: Va) -> Result<u16, VmiError> {
161        self.read_u16_in(self.access_context(address))
162    }
163
164    /// Reads a 32-bit unsigned integer from the virtual machine.
165    pub fn read_u32(&self, address: Va) -> Result<u32, VmiError> {
166        self.read_u32_in(self.access_context(address))
167    }
168
169    /// Reads a 64-bit unsigned integer from the virtual machine.
170    pub fn read_u64(&self, address: Va) -> Result<u64, VmiError> {
171        self.read_u64_in(self.access_context(address))
172    }
173
174    /// Reads an unsigned integer of the specified size from the virtual machine.
175    ///
176    /// This method reads an unsigned integer of the specified size (in bytes)
177    /// from the virtual machine. Note that the size must be 1, 2, 4, or 8.
178    ///
179    /// The result is returned as a [`u64`] to accommodate the widest possible
180    /// integer size.
181    pub fn read_uint(&self, address: Va, size: usize) -> Result<u64, VmiError> {
182        self.read_uint_in(self.access_context(address), size)
183    }
184
185    /// Reads a field of a structure from the virtual machine.
186    ///
187    /// This method reads a field from the virtual machine. The field is
188    /// defined by the provided [`Field`] structure, which specifies the
189    /// offset and size of the field within the memory region.
190    ///
191    /// The result is returned as a [`u64`] to accommodate the widest possible
192    /// integer size.
193    pub fn read_field(&self, base_address: Va, field: &Field) -> Result<u64, VmiError> {
194        self.read_field_in(self.access_context(base_address), field)
195    }
196
197    /// Reads an address-sized unsigned integer from the virtual machine.
198    pub fn read_address(&self, address: Va) -> Result<u64, VmiError> {
199        self.read_address_in(self.access_context(address))
200    }
201
202    /// Reads an address-sized unsigned integer from the virtual machine.
203    pub fn read_address_native(&self, address: Va) -> Result<u64, VmiError> {
204        self.read_address_native_in(self.access_context(address))
205    }
206
207    /// Reads a 32-bit address from the virtual machine.
208    pub fn read_address32(&self, address: Va) -> Result<u64, VmiError> {
209        self.read_address32_in(self.access_context(address))
210    }
211
212    /// Reads a 64-bit address from the virtual machine.
213    pub fn read_address64(&self, address: Va) -> Result<u64, VmiError> {
214        self.read_address64_in(self.access_context(address))
215    }
216
217    /// Reads a virtual address from the virtual machine.
218    pub fn read_va(&self, address: Va) -> Result<Va, VmiError> {
219        self.read_va_in(self.access_context(address))
220    }
221
222    /// Reads a virtual address from the virtual machine.
223    pub fn read_va_native(&self, address: Va) -> Result<Va, VmiError> {
224        self.read_va_native_in(self.access_context(address))
225    }
226
227    /// Reads a 32-bit virtual address from the virtual machine.
228    pub fn read_va32(&self, address: Va) -> Result<Va, VmiError> {
229        self.read_va32_in(self.access_context(address))
230    }
231
232    /// Reads a 64-bit virtual address from the virtual machine.
233    pub fn read_va64(&self, address: Va) -> Result<Va, VmiError> {
234        self.read_va64_in(self.access_context(address))
235    }
236
237    /// Reads a null-terminated string of bytes from the virtual machine with a
238    /// specified limit.
239    pub fn read_string_bytes_limited(
240        &self,
241        address: Va,
242        limit: usize,
243    ) -> Result<Vec<u8>, VmiError> {
244        self.read_string_bytes_limited_in(self.access_context(address), limit)
245    }
246
247    /// Reads a null-terminated string of bytes from the virtual machine.
248    pub fn read_string_bytes(&self, address: Va) -> Result<Vec<u8>, VmiError> {
249        self.read_string_bytes_in(self.access_context(address))
250    }
251
252    /// Reads a null-terminated wide string (UTF-16) from the virtual machine
253    /// with a specified limit.
254    pub fn read_string_utf16_bytes_limited(
255        &self,
256        address: Va,
257        limit: usize,
258    ) -> Result<Vec<u16>, VmiError> {
259        self.read_string_utf16_bytes_limited_in(self.access_context(address), limit)
260    }
261
262    /// Reads a null-terminated wide string (UTF-16) from the virtual machine.
263    pub fn read_string_utf16_bytes(&self, address: Va) -> Result<Vec<u16>, VmiError> {
264        self.read_string_utf16_bytes_in(self.access_context(address))
265    }
266
267    /// Reads a null-terminated string from the virtual machine with a specified
268    /// limit.
269    pub fn read_string_limited(&self, address: Va, limit: usize) -> Result<String, VmiError> {
270        self.read_string_limited_in(self.access_context(address), limit)
271    }
272
273    /// Reads a null-terminated string from the virtual machine.
274    pub fn read_string(&self, address: Va) -> Result<String, VmiError> {
275        self.read_string_in(self.access_context(address))
276    }
277
278    /// Reads a null-terminated wide string (UTF-16) from the virtual machine
279    /// with a specified limit.
280    pub fn read_string_utf16_limited(&self, address: Va, limit: usize) -> Result<String, VmiError> {
281        self.read_string_utf16_limited_in(self.access_context(address), limit)
282    }
283
284    /// Reads a null-terminated wide string (UTF-16) from the virtual machine.
285    pub fn read_string_utf16(&self, address: Va) -> Result<String, VmiError> {
286        self.read_string_utf16_in(self.access_context(address))
287    }
288
289    /// Reads a struct from the virtual machine.
290    pub fn read_struct<T>(&self, address: Va) -> Result<T, VmiError>
291    where
292        T: IntoBytes + FromBytes,
293    {
294        self.read_struct_in(self.access_context(address))
295    }
296
297    // endregion: Read
298
299    // region: Read in
300
301    /// Reads memory from the virtual machine.
302    pub fn read_in(
303        &self,
304        ctx: impl Into<AccessContext>,
305        buffer: &mut [u8],
306    ) -> Result<(), VmiError> {
307        self.core().read(ctx, buffer)
308    }
309
310    /// Reads a single byte from the virtual machine.
311    pub fn read_u8_in(&self, ctx: impl Into<AccessContext>) -> Result<u8, VmiError> {
312        self.core().read_u8(ctx)
313    }
314
315    /// Reads a 16-bit unsigned integer from the virtual machine.
316    pub fn read_u16_in(&self, ctx: impl Into<AccessContext>) -> Result<u16, VmiError> {
317        self.core().read_u16(ctx)
318    }
319
320    /// Reads a 32-bit unsigned integer from the virtual machine.
321    pub fn read_u32_in(&self, ctx: impl Into<AccessContext>) -> Result<u32, VmiError> {
322        self.core().read_u32(ctx)
323    }
324
325    /// Reads a 64-bit unsigned integer from the virtual machine.
326    pub fn read_u64_in(&self, ctx: impl Into<AccessContext>) -> Result<u64, VmiError> {
327        self.core().read_u64(ctx)
328    }
329
330    /// Reads an unsigned integer of the specified size from the virtual machine.
331    ///
332    /// This method reads an unsigned integer of the specified size (in bytes)
333    /// from the virtual machine. Note that the size must be 1, 2, 4, or 8.
334    ///
335    /// The result is returned as a [`u64`] to accommodate the widest possible
336    /// integer size.
337    pub fn read_uint_in(
338        &self,
339        ctx: impl Into<AccessContext>,
340        size: usize,
341    ) -> Result<u64, VmiError> {
342        self.core().read_uint(ctx, size)
343    }
344
345    /// Reads a field of a structure from the virtual machine.
346    ///
347    /// This method reads a field from the virtual machine. The field is
348    /// defined by the provided [`Field`] structure, which specifies the
349    /// offset and size of the field within the memory region.
350    ///
351    /// The result is returned as a [`u64`] to accommodate the widest possible
352    /// integer size.
353    pub fn read_field_in(
354        &self,
355        ctx: impl Into<AccessContext>,
356        field: &Field,
357    ) -> Result<u64, VmiError> {
358        self.core().read_field(ctx, field)
359    }
360
361    /// Reads an address-sized unsigned integer from the virtual machine.
362    pub fn read_address_in(&self, ctx: impl Into<AccessContext>) -> Result<u64, VmiError> {
363        self.core()
364            .read_address(ctx, self.registers().effective_address_width())
365    }
366
367    /// Reads an address-sized unsigned integer from the virtual machine.
368    pub fn read_address_native_in(&self, ctx: impl Into<AccessContext>) -> Result<u64, VmiError> {
369        self.core()
370            .read_address(ctx, self.registers().address_width())
371    }
372
373    /// Reads a 32-bit address from the virtual machine.
374    pub fn read_address32_in(&self, ctx: impl Into<AccessContext>) -> Result<u64, VmiError> {
375        self.core().read_address32(ctx)
376    }
377
378    /// Reads a 64-bit address from the virtual machine.
379    pub fn read_address64_in(&self, ctx: impl Into<AccessContext>) -> Result<u64, VmiError> {
380        self.core().read_address64(ctx)
381    }
382
383    /// Reads a virtual address from the virtual machine.
384    pub fn read_va_in(&self, ctx: impl Into<AccessContext>) -> Result<Va, VmiError> {
385        self.core()
386            .read_va(ctx, self.registers().effective_address_width())
387    }
388
389    /// Reads a virtual address from the virtual machine.
390    pub fn read_va_native_in(&self, ctx: impl Into<AccessContext>) -> Result<Va, VmiError> {
391        self.core().read_va(ctx, self.registers().address_width())
392    }
393
394    /// Reads a 32-bit virtual address from the virtual machine.
395    pub fn read_va32_in(&self, ctx: impl Into<AccessContext>) -> Result<Va, VmiError> {
396        self.core().read_va32(ctx)
397    }
398
399    /// Reads a 64-bit virtual address from the virtual machine.
400    pub fn read_va64_in(&self, ctx: impl Into<AccessContext>) -> Result<Va, VmiError> {
401        self.core().read_va64(ctx)
402    }
403
404    /// Reads a null-terminated string of bytes from the virtual machine with a
405    /// specified limit.
406    pub fn read_string_bytes_limited_in(
407        &self,
408        ctx: impl Into<AccessContext>,
409        limit: usize,
410    ) -> Result<Vec<u8>, VmiError> {
411        self.core().read_string_bytes_limited(ctx, limit)
412    }
413
414    /// Reads a null-terminated string of bytes from the virtual machine.
415    pub fn read_string_bytes_in(&self, ctx: impl Into<AccessContext>) -> Result<Vec<u8>, VmiError> {
416        self.core().read_string_bytes(ctx)
417    }
418
419    /// Reads a null-terminated wide string (UTF-16) from the virtual machine
420    /// with a specified limit.
421    pub fn read_string_utf16_bytes_limited_in(
422        &self,
423        ctx: impl Into<AccessContext>,
424        limit: usize,
425    ) -> Result<Vec<u16>, VmiError> {
426        self.core().read_string_utf16_bytes_limited(ctx, limit)
427    }
428
429    /// Reads a null-terminated wide string (UTF-16) from the virtual machine.
430    pub fn read_string_utf16_bytes_in(
431        &self,
432        ctx: impl Into<AccessContext>,
433    ) -> Result<Vec<u16>, VmiError> {
434        self.core().read_string_utf16_bytes(ctx)
435    }
436
437    /// Reads a null-terminated string from the virtual machine with a specified
438    /// limit.
439    pub fn read_string_limited_in(
440        &self,
441        ctx: impl Into<AccessContext>,
442        limit: usize,
443    ) -> Result<String, VmiError> {
444        self.core().read_string_limited(ctx, limit)
445    }
446
447    /// Reads a null-terminated string from the virtual machine.
448    pub fn read_string_in(&self, ctx: impl Into<AccessContext>) -> Result<String, VmiError> {
449        self.core().read_string(ctx)
450    }
451
452    /// Reads a null-terminated wide string (UTF-16) from the virtual machine
453    /// with a specified limit.
454    pub fn read_string_utf16_limited_in(
455        &self,
456        ctx: impl Into<AccessContext>,
457        limit: usize,
458    ) -> Result<String, VmiError> {
459        self.core().read_string_utf16_limited(ctx, limit)
460    }
461
462    /// Reads a null-terminated wide string (UTF-16) from the virtual machine.
463    pub fn read_string_utf16_in(&self, ctx: impl Into<AccessContext>) -> Result<String, VmiError> {
464        self.core().read_string_utf16(ctx)
465    }
466
467    /// Reads a struct from the virtual machine.
468    pub fn read_struct_in<T>(&self, ctx: impl Into<AccessContext>) -> Result<T, VmiError>
469    where
470        T: IntoBytes + FromBytes,
471    {
472        self.core().read_struct(ctx)
473    }
474
475    // endregion: Read in
476}
477
478///////////////////////////////////////////////////////////////////////////////
479// VmiRead + VmiWrite
480///////////////////////////////////////////////////////////////////////////////
481
482impl<'a, Os> VmiState<'a, Os>
483where
484    Os: VmiOs,
485    Os::Driver: VmiRead + VmiWrite,
486{
487    /// Writes memory to the virtual machine.
488    pub fn write(&self, address: Va, buffer: &[u8]) -> Result<(), VmiError> {
489        self.write_in(self.access_context(address), buffer)
490    }
491
492    /// Writes memory to the virtual machine.
493    pub fn write_in(&self, ctx: impl Into<AccessContext>, buffer: &[u8]) -> Result<(), VmiError> {
494        self.core().write(ctx, buffer)
495    }
496
497    /// Writes a single byte to the virtual machine.
498    pub fn write_u8(&self, address: Va, value: u8) -> Result<(), VmiError> {
499        self.core().write_u8(self.access_context(address), value)
500    }
501
502    /// Writes a 16-bit unsigned integer to the virtual machine.
503    pub fn write_u16(&self, address: Va, value: u16) -> Result<(), VmiError> {
504        self.core().write_u16(self.access_context(address), value)
505    }
506
507    /// Writes a 32-bit unsigned integer to the virtual machine.
508    pub fn write_u32(&self, address: Va, value: u32) -> Result<(), VmiError> {
509        self.core().write_u32(self.access_context(address), value)
510    }
511
512    /// Writes a 64-bit unsigned integer to the virtual machine.
513    pub fn write_u64(&self, address: Va, value: u64) -> Result<(), VmiError> {
514        self.core().write_u64(self.access_context(address), value)
515    }
516
517    /// Writes a struct to the virtual machine.
518    pub fn write_struct<T>(&self, address: Va, value: T) -> Result<(), VmiError>
519    where
520        T: FromBytes + IntoBytes + Immutable,
521    {
522        self.core()
523            .write_struct(self.access_context(address), value)
524    }
525}
526
527///////////////////////////////////////////////////////////////////////////////
528// VmiSetRegisters
529///////////////////////////////////////////////////////////////////////////////
530
531impl<'a, Os> VmiState<'a, Os>
532where
533    Os: VmiOs,
534    Os::Driver: VmiSetRegisters,
535{
536    /// Sets the registers of a virtual CPU.
537    pub fn set_registers(
538        &self,
539        vcpu: VcpuId,
540        registers: <Os::Architecture as Architecture>::Registers,
541    ) -> Result<(), VmiError> {
542        self.core().set_registers(vcpu, registers)
543    }
544}
545
546/// Wrapper providing access to OS-specific operations.
547pub struct VmiOsState<'a, Os>(VmiState<'a, Os>)
548where
549    Os: VmiOs;
550
551impl<'a, Os> VmiOsState<'a, Os>
552where
553    Os: VmiOs,
554{
555    /// Returns the VMI core.
556    pub fn core(&self) -> &'a VmiCore<Os::Driver> {
557        self.0.core()
558    }
559
560    /// Returns the underlying OS-specific implementation.
561    pub fn underlying_os(&self) -> &'a Os {
562        self.0.underlying_os()
563    }
564
565    /// Returns the VMI session.
566    pub fn session(&self) -> &VmiSession<'a, Os> {
567        self.0.session()
568    }
569
570    /// Returns the VMI state.
571    pub fn state(&self) -> VmiState<'a, Os> {
572        self.0
573    }
574
575    /// Returns the CPU registers associated with the current event.
576    pub fn registers(&self) -> &<Os::Architecture as Architecture>::Registers {
577        self.0.registers()
578    }
579
580    /// Retrieves a specific function argument according to the calling
581    /// convention of the operating system.
582    pub fn function_argument_for_registers(
583        &self,
584        registers: &<Os::Architecture as Architecture>::Registers,
585        index: u64,
586    ) -> Result<u64, VmiError> {
587        Os::function_argument(self.0.with_registers(registers), index)
588    }
589
590    /// Retrieves the return value of a function.
591    pub fn function_return_value_for_registers(
592        &self,
593        registers: &<Os::Architecture as Architecture>::Registers,
594    ) -> Result<u64, VmiError> {
595        Os::function_return_value(self.0.with_registers(registers))
596    }
597}