hypervisor/
lib.rs

1// Copyright 2020 The ChromiumOS Authors
2// Use of this source code is governed by a BSD-style license that can be
3// found in the LICENSE file.
4
5//! A crate for abstracting the underlying kernel hypervisor used in crosvm.
6
7#[cfg(target_arch = "aarch64")]
8pub mod aarch64;
9pub mod caps;
10#[cfg(all(unix, target_arch = "aarch64", feature = "geniezone"))]
11pub mod geniezone;
12#[cfg(all(unix, target_arch = "aarch64", feature = "gunyah"))]
13pub mod gunyah;
14#[cfg(target_arch = "aarch64")]
15#[cfg(all(unix, target_arch = "aarch64", feature = "halla"))]
16pub mod halla;
17#[cfg(all(windows, feature = "haxm"))]
18pub mod haxm;
19#[cfg(any(target_os = "android", target_os = "linux"))]
20pub mod kvm;
21#[cfg(target_arch = "riscv64")]
22pub mod riscv64;
23#[cfg(all(windows, feature = "whpx"))]
24pub mod whpx;
25#[cfg(target_arch = "x86_64")]
26pub mod x86_64;
27
28use base::AsRawDescriptor;
29use base::Event;
30use base::MappedRegion;
31use base::Protection;
32use base::Result;
33use base::SafeDescriptor;
34use serde::Deserialize;
35use serde::Serialize;
36use vm_memory::GuestAddress;
37use vm_memory::GuestMemory;
38
39#[cfg(target_arch = "aarch64")]
40pub use crate::aarch64::*;
41pub use crate::caps::*;
42#[cfg(target_arch = "riscv64")]
43pub use crate::riscv64::*;
44#[cfg(target_arch = "x86_64")]
45pub use crate::x86_64::*;
46
47/// An index in the list of guest-mapped memory regions.
48pub type MemSlot = u32;
49
50/// Range of GPA space. Starting from `guest_address` up to `size`.
51pub struct MemRegion {
52    pub guest_address: GuestAddress,
53    pub size: u64,
54}
55
56/// Signal to the hypervisor on kernels that support the KVM_CAP_USER_CONFIGURE_NONCOHERENT_DMA (or
57/// equivalent) that during user memory region (memslot) configuration, a guest page's memtype
58/// should be considered in SLAT effective memtype determination rather than implicitly respecting
59/// only the host page's memtype.
60///
61/// This explicit control is needed for Virtio devices (e.g. gpu) that configure memslots for host
62/// WB page mappings with guest WC page mappings. See b/316337317, b/360295883 for more detail.
63#[derive(Copy, Clone, Debug, Serialize, Deserialize, PartialEq, Eq)]
64pub enum MemCacheType {
65    /// Don't provide any explicit instruction to the hypervisor on how it should determine a
66    /// memslot's effective memtype.
67    ///
68    /// On KVM-VMX (Intel), this means that the memslot is flagged with VMX_EPT_IPAT_BIT such that
69    /// only the host memtype is respected.
70    CacheCoherent,
71    /// explicitly instruct the hypervisor to respect the guest page's memtype when determining the
72    /// memslot's effective memtype.
73    ///
74    /// On KVM-VMX (Intel), this means the memslot is NOT flagged with VMX_EPT_IPAT_BIT, and the
75    /// effective memtype will generally decay to the weaker amongst the host/guest memtypes and
76    /// the MTRR for the physical address.
77    CacheNonCoherent,
78}
79
80/// This is intended for use with virtio-balloon, where a guest driver determines unused ranges and
81/// requests they be freed. Use without the guest's knowledge is sure to break something.
82pub enum BalloonEvent {
83    /// Balloon event when the region is acquired from the guest. The guest cannot access this
84    /// region any more. The guest memory can be reclaimed by the host OS. As per virtio-balloon
85    /// spec, the given address and size are intended to be page-aligned.
86    Inflate(MemRegion),
87    /// Balloon event when the region is returned to the guest. VMM should reallocate memory and
88    /// register it with the hypervisor for accesses by the guest.
89    Deflate(MemRegion),
90    /// Balloon event when the requested memory size is achieved. This can be achieved through
91    /// either inflation or deflation. The `u64` will be the current size of the balloon in bytes.
92    BalloonTargetReached(u64),
93}
94
95/// Supported hypervisors.
96///
97/// When adding a new one, also update the HypervisorFfi in crosvm_control/src/lib.rs
98#[derive(Serialize, Deserialize, Debug, Clone)]
99pub enum HypervisorKind {
100    Geniezone,
101    Gunyah,
102    Halla,
103    Kvm,
104    Haxm,
105    Whpx,
106}
107
108/// A trait for checking hypervisor capabilities.
109pub trait Hypervisor: Send {
110    /// Makes a shallow clone of this `Hypervisor`.
111    fn try_clone(&self) -> Result<Self>
112    where
113        Self: Sized;
114
115    /// Checks if a particular `HypervisorCap` is available.
116    fn check_capability(&self, cap: HypervisorCap) -> bool;
117}
118
119/// A wrapper for using a VM and getting/setting its state.
120pub trait Vm: Send {
121    /// Makes a shallow clone of this `Vm`.
122    fn try_clone(&self) -> Result<Self>
123    where
124        Self: Sized;
125
126    /// Makes a shallow clone of the fd of this `Vm`.
127    fn try_clone_descriptor(&self) -> Result<SafeDescriptor>;
128
129    /// Returns hypervisor managing this `Vm`.
130    fn hypervisor_kind(&self) -> HypervisorKind;
131
132    /// Checks if a particular `VmCap` is available.
133    ///
134    /// This is distinct from the `Hypervisor` version of this method because some extensions depend
135    /// on the particular `Vm` instance. This method is encouraged because it more accurately
136    /// reflects the usable capabilities.
137    fn check_capability(&self, c: VmCap) -> bool;
138
139    /// Enable the VM capabilities.
140    fn enable_capability(&self, _capability: VmCap, _flags: u32) -> Result<bool> {
141        Err(std::io::Error::from(std::io::ErrorKind::Unsupported).into())
142    }
143
144    /// Get the guest physical address size in bits.
145    fn get_guest_phys_addr_bits(&self) -> u8;
146
147    /// Gets the guest-mapped memory for the Vm.
148    fn get_memory(&self) -> &GuestMemory;
149
150    /// Inserts the given `MappedRegion` into the VM's address space at `guest_addr`.
151    ///
152    /// The slot that was assigned the memory mapping is returned on success.  The slot can be given
153    /// to `Vm::remove_memory_region` to remove the memory from the VM's address space and take back
154    /// ownership of `mem_region`.
155    ///
156    /// Note that memory inserted into the VM's address space must not overlap with any other memory
157    /// slot's region.
158    ///
159    /// If `read_only` is true, the guest will be able to read the memory as normal, but attempts to
160    /// write will trigger a mmio VM exit, leaving the memory untouched.
161    ///
162    /// If `log_dirty_pages` is true, the slot number can be used to retrieve the pages written to
163    /// by the guest with `get_dirty_log`.
164    ///
165    /// `cache` can be used to set guest mem cache attribute if supported. Default is cache coherent
166    /// memory. Noncoherent memory means this memory might not be coherent from all access points,
167    /// e.g this could be the case when host GPU doesn't set the memory to be coherent with CPU
168    /// access. Setting this attribute would allow hypervisor to adjust guest mem control to ensure
169    /// synchronized guest access in noncoherent DMA case.
170    fn add_memory_region(
171        &mut self,
172        guest_addr: GuestAddress,
173        mem_region: Box<dyn MappedRegion>,
174        read_only: bool,
175        log_dirty_pages: bool,
176        cache: MemCacheType,
177    ) -> Result<MemSlot>;
178
179    /// Does a synchronous msync of the memory mapped at `slot`, syncing `size` bytes starting at
180    /// `offset` from the start of the region.  `offset` must be page aligned.
181    fn msync_memory_region(&mut self, slot: MemSlot, offset: usize, size: usize) -> Result<()>;
182
183    /// Gives a MADV_PAGEOUT advice to the memory region mapped at `slot`, with the address range
184    /// starting at `offset` from the start of the region, and with size `size`. `offset`
185    /// must be page aligned.
186    #[cfg(any(target_os = "android", target_os = "linux"))]
187    fn madvise_pageout_memory_region(
188        &mut self,
189        slot: MemSlot,
190        offset: usize,
191        size: usize,
192    ) -> Result<()>;
193
194    /// Gives a MADV_REMOVE advice to the memory region mapped at `slot`, with the address range
195    /// starting at `offset` from the start of the region, and with size `size`. `offset`
196    /// must be page aligned.
197    #[cfg(any(target_os = "android", target_os = "linux"))]
198    fn madvise_remove_memory_region(
199        &mut self,
200        slot: MemSlot,
201        offset: usize,
202        size: usize,
203    ) -> Result<()>;
204
205    /// Removes and drops the `UserMemoryRegion` that was previously added at the given slot.
206    fn remove_memory_region(&mut self, slot: MemSlot) -> Result<Box<dyn MappedRegion>>;
207
208    /// Creates an emulated device.
209    fn create_device(&self, kind: DeviceKind) -> Result<SafeDescriptor>;
210
211    /// Gets the bitmap of dirty pages since the last call to `get_dirty_log` for the memory at
212    /// `slot`.  Only works on VMs that support `VmCap::DirtyLog`.
213    ///
214    /// The size of `dirty_log` must be at least as many bits as there are pages in the memory
215    /// region `slot` represents. For example, if the size of `slot` is 16 pages, `dirty_log` must
216    /// be 2 bytes or greater.
217    fn get_dirty_log(&self, slot: MemSlot, dirty_log: &mut [u8]) -> Result<()>;
218
219    /// Registers an event to be signaled whenever a certain address is written to.
220    ///
221    /// The `datamatch` parameter can be used to limit signaling `evt` to only the cases where the
222    /// value being written is equal to `datamatch`. Note that the size of `datamatch` is important
223    /// and must match the expected size of the guest's write.
224    ///
225    /// In all cases where `evt` is signaled, the ordinary vmexit to userspace that would be
226    /// triggered is prevented.
227    fn register_ioevent(
228        &mut self,
229        evt: &Event,
230        addr: IoEventAddress,
231        datamatch: Datamatch,
232    ) -> Result<()>;
233
234    /// Unregisters an event previously registered with `register_ioevent`.
235    ///
236    /// The `evt`, `addr`, and `datamatch` set must be the same as the ones passed into
237    /// `register_ioevent`.
238    fn unregister_ioevent(
239        &mut self,
240        evt: &Event,
241        addr: IoEventAddress,
242        datamatch: Datamatch,
243    ) -> Result<()>;
244
245    /// Trigger any matching registered io events based on an MMIO or PIO write at `addr`. The
246    /// `data` slice represents the contents and length of the write, which is used to compare with
247    /// the registered io events' Datamatch values. If the hypervisor does in-kernel IO event
248    /// delivery, this is a no-op.
249    fn handle_io_events(&self, addr: IoEventAddress, data: &[u8]) -> Result<()>;
250
251    /// Retrieves the current timestamp of the paravirtual clock as seen by the current guest.
252    /// Only works on VMs that support `VmCap::PvClock`.
253    fn get_pvclock(&self) -> Result<ClockState>;
254
255    /// Sets the current timestamp of the paravirtual clock as seen by the current guest.
256    /// Only works on VMs that support `VmCap::PvClock`.
257    fn set_pvclock(&self, state: &ClockState) -> Result<()>;
258
259    /// Maps `size` bytes starting at `fs_offset` bytes from within the given `fd`
260    /// at `offset` bytes from the start of the arena with `prot` protections.
261    /// `offset` must be page aligned.
262    ///
263    /// # Arguments
264    /// * `offset` - Page aligned offset into the arena in bytes.
265    /// * `size` - Size of memory region in bytes.
266    /// * `fd` - File descriptor to mmap from.
267    /// * `fd_offset` - Offset in bytes from the beginning of `fd` to start the mmap.
268    /// * `prot` - Protection (e.g. readable/writable) of the memory region.
269    fn add_fd_mapping(
270        &mut self,
271        slot: u32,
272        offset: usize,
273        size: usize,
274        fd: &dyn AsRawDescriptor,
275        fd_offset: u64,
276        prot: Protection,
277    ) -> Result<()>;
278
279    /// Remove `size`-byte mapping starting at `offset`.
280    fn remove_mapping(&mut self, slot: u32, offset: usize, size: usize) -> Result<()>;
281
282    /// Events from virtio-balloon that affect the state for guest memory and host memory.
283    fn handle_balloon_event(&mut self, event: BalloonEvent) -> Result<()>;
284
285    /// Registers with the hypervisor for CrosVM to handle any guest hypercall in the range.
286    fn enable_hypercalls(&mut self, nr: u64, count: usize) -> Result<()>;
287
288    /// Registers with the hypervisor for CrosVM to handle the guest hypercall.
289    fn enable_hypercall(&mut self, nr: u64) -> Result<()> {
290        self.enable_hypercalls(nr, 1)
291    }
292}
293
294/// Operation for Io and Mmio
295#[derive(Debug)]
296pub enum IoOperation<'a> {
297    /// Data to be read from a device on the bus.
298    ///
299    /// The `handle_fn` should fill the entire slice with the read data.
300    Read(&'a mut [u8]),
301
302    /// Data to be written to a device on the bus.
303    Write(&'a [u8]),
304}
305
306/// Parameters describing an MMIO or PIO from the guest.
307#[derive(Debug)]
308pub struct IoParams<'a> {
309    pub address: u64,
310    pub operation: IoOperation<'a>,
311}
312
313/// Architecture-agnostic wrapper for any hypercall ABI between CrosVM and the guest.
314#[derive(Debug)]
315pub struct HypercallAbi {
316    hypercall_id: usize,
317    args: Vec<usize>,
318    res: Vec<usize>,
319}
320
321impl HypercallAbi {
322    /// Creates a new `HypercallAbi` instance, with the default error result.
323    pub fn new(hypercall_id: usize, args: &[usize], default_res: &[usize]) -> Self {
324        Self {
325            hypercall_id,
326            args: args.to_owned(),
327            res: default_res.to_owned(),
328        }
329    }
330
331    /// Returns the hypercall unique identifier, for routing.
332    pub fn hypercall_id(&self) -> usize {
333        self.hypercall_id
334    }
335
336    /// Returns the n-th guest-provided architecture-specific arguments.
337    pub fn get_argument(&self, n: usize) -> Option<&usize> {
338        self.args.get(n)
339    }
340
341    /// Returns the architecture-specific results for the guest, if set.
342    pub fn get_results(&self) -> &[usize] {
343        self.res.as_slice()
344    }
345
346    /// Sets the architecture-specific results for the guest.
347    pub fn set_results(&mut self, res: &[usize]) {
348        self.res = res.to_owned()
349    }
350}
351
352/// Handle to a virtual CPU that may be used to request a VM exit from within a signal handler.
353#[cfg(any(target_os = "android", target_os = "linux"))]
354pub struct VcpuSignalHandle {
355    inner: Box<dyn VcpuSignalHandleInner>,
356}
357
358#[cfg(any(target_os = "android", target_os = "linux"))]
359impl VcpuSignalHandle {
360    /// Request an immediate exit for this VCPU.
361    ///
362    /// This function is safe to call from a signal handler.
363    pub fn signal_immediate_exit(&self) {
364        self.inner.signal_immediate_exit()
365    }
366}
367
368/// Signal-safe mechanism for requesting an immediate VCPU exit.
369///
370/// Each hypervisor backend must implement this for its VCPU type.
371#[cfg(any(target_os = "android", target_os = "linux"))]
372pub(crate) trait VcpuSignalHandleInner {
373    /// Signal the associated VCPU to exit if it is currently running.
374    ///
375    /// # Safety
376    ///
377    /// The implementation of this function must be async signal safe.
378    /// <https://man7.org/linux/man-pages/man7/signal-safety.7.html>
379    fn signal_immediate_exit(&self);
380}
381
382/// A virtual CPU holding a virtualized hardware thread's state, such as registers and interrupt
383/// state, which may be used to execute virtual machines.
384pub trait Vcpu: downcast_rs::DowncastSync {
385    /// Makes a shallow clone of this `Vcpu`.
386    fn try_clone(&self) -> Result<Self>
387    where
388        Self: Sized;
389
390    /// Casts this architecture specific trait object to the base trait object `Vcpu`.
391    fn as_vcpu(&self) -> &dyn Vcpu;
392
393    /// Runs the VCPU until it exits, returning the reason for the exit.
394    fn run(&mut self) -> Result<VcpuExit>;
395
396    /// Returns the vcpu id.
397    fn id(&self) -> usize;
398
399    /// Sets the bit that requests an immediate exit.
400    fn set_immediate_exit(&self, exit: bool);
401
402    /// Returns a handle that can be used to cause this VCPU to exit from `run()` from a signal
403    /// handler.
404    #[cfg(any(target_os = "android", target_os = "linux"))]
405    fn signal_handle(&self) -> VcpuSignalHandle;
406
407    /// Handles an incoming MMIO request from the guest.
408    ///
409    /// This function should be called after `Vcpu::run` returns `VcpuExit::Mmio`, and in the same
410    /// thread as run().
411    ///
412    /// Once called, it will determine whether a MMIO read or MMIO write was the reason for the MMIO
413    /// exit, call `handle_fn` with the respective IoParams to perform the MMIO read or write, and
414    /// set the return data in the vcpu so that the vcpu can resume running.
415    fn handle_mmio(&self, handle_fn: &mut dyn FnMut(IoParams) -> Result<()>) -> Result<()>;
416
417    /// Handles an incoming PIO from the guest.
418    ///
419    /// This function should be called after `Vcpu::run` returns `VcpuExit::Io`, and in the same
420    /// thread as run().
421    ///
422    /// Once called, it will determine whether an input or output was the reason for the Io exit,
423    /// call `handle_fn` with the respective IoParams to perform the input/output operation, and set
424    /// the return data in the vcpu so that the vcpu can resume running.
425    fn handle_io(&self, handle_fn: &mut dyn FnMut(IoParams)) -> Result<()>;
426
427    /// Handles an incoming hypercall from the guest.
428    fn handle_hypercall(
429        &self,
430        _handle_fn: &mut dyn FnMut(&mut HypercallAbi) -> anyhow::Result<()>,
431    ) -> anyhow::Result<()> {
432        anyhow::bail!(
433            "handle_hypercall not implemented for {}",
434            std::any::type_name::<Self>(),
435        )
436    }
437
438    /// Signals to the hypervisor that this Vcpu is being paused by userspace.
439    fn on_suspend(&self) -> Result<()>;
440
441    /// Enables a hypervisor-specific extension on this Vcpu.  `cap` is a constant defined by the
442    /// hypervisor API (e.g., kvm.h).  `args` are the arguments for enabling the feature, if any.
443    ///
444    /// # Safety
445    /// This function is marked as unsafe because `args` may be interpreted as pointers for some
446    /// capabilities. The caller must ensure that any pointers passed in the `args` array are
447    /// allocated as the kernel expects, and that mutable pointers are owned.
448    unsafe fn enable_raw_capability(&self, cap: u32, args: &[u64; 4]) -> Result<()>;
449}
450
451downcast_rs::impl_downcast!(sync Vcpu);
452
453/// An address either in programmable I/O space or in memory mapped I/O space.
454#[derive(Copy, Clone, Debug, Serialize, Deserialize, PartialEq, Eq, std::hash::Hash)]
455pub enum IoEventAddress {
456    Pio(u64),
457    Mmio(u64),
458}
459
460/// Used in `Vm::register_ioevent` to indicate a size and optionally value to match.
461#[derive(Clone, Copy, PartialEq, Eq, Serialize, Deserialize)]
462pub enum Datamatch {
463    AnyLength,
464    U8(Option<u8>),
465    U16(Option<u16>),
466    U32(Option<u32>),
467    U64(Option<u64>),
468}
469
470#[derive(Copy, Clone, Debug)]
471pub enum VcpuShutdownErrorKind {
472    DoubleFault,
473    TripleFault,
474    Other,
475}
476
477/// A Vcpu shutdown may signify an error, such as a double or triple fault,
478/// or hypervisor specific reasons. This error covers all such cases.
479#[derive(Copy, Clone, Debug)]
480pub struct VcpuShutdownError {
481    kind: VcpuShutdownErrorKind,
482    raw_error_code: u64,
483}
484
485impl VcpuShutdownError {
486    pub fn new(kind: VcpuShutdownErrorKind, raw_error_code: u64) -> VcpuShutdownError {
487        Self {
488            kind,
489            raw_error_code,
490        }
491    }
492    pub fn kind(&self) -> VcpuShutdownErrorKind {
493        self.kind
494    }
495    pub fn get_raw_error_code(&self) -> u64 {
496        self.raw_error_code
497    }
498}
499
500// Note that when adding entries to the VcpuExit enum you may want to add corresponding entries in
501// crosvm::stats::exit_to_index and crosvm::stats::exit_index_to_str if you don't want the new
502// exit type to be categorized as "Unknown".
503
504/// A reason why a VCPU exited. One of these returns every time `Vcpu::run` is called.
505#[derive(Debug, Clone, Copy)]
506pub enum VcpuExit {
507    /// An io instruction needs to be emulated.
508    /// vcpu handle_io should be called to handle the io operation
509    Io,
510    /// A mmio instruction needs to be emulated.
511    /// vcpu handle_mmio should be called to handle the mmio operation
512    Mmio,
513    IoapicEoi {
514        vector: u8,
515    },
516    Exception,
517    Hypercall,
518    Debug,
519    Hlt,
520    IrqWindowOpen,
521    Shutdown(std::result::Result<(), VcpuShutdownError>),
522    FailEntry {
523        hardware_entry_failure_reason: u64,
524    },
525    Intr,
526    SetTpr,
527    TprAccess,
528    InternalError,
529    SystemEventShutdown,
530    SystemEventReset,
531    SystemEventCrash,
532    /// An invalid vcpu register was set while running.
533    InvalidVpRegister,
534    /// incorrect setup for vcpu requiring an unsupported feature
535    UnsupportedFeature,
536    /// vcpu run was user cancelled
537    Canceled,
538    /// an unrecoverable exception was encountered (different from Exception)
539    UnrecoverableException,
540    /// vcpu stopped due to an msr access.
541    MsrAccess,
542    /// vcpu stopped due to a cpuid request.
543    #[cfg(target_arch = "x86_64")]
544    Cpuid {
545        entry: CpuIdEntry,
546    },
547    /// vcpu stopped due to calling rdtsc
548    RdTsc,
549    /// vcpu stopped for an apic smi trap
550    ApicSmiTrap,
551    /// vcpu stopped due to an apic trap
552    ApicInitSipiTrap,
553    /// vcpu stoppted due to bus lock
554    BusLock,
555    /// Riscv supervisor call.
556    Sbi {
557        extension_id: u64,
558        function_id: u64,
559        args: [u64; 6],
560    },
561    /// Emulate CSR access from guest.
562    RiscvCsr {
563        csr_num: u64,
564        new_value: u64,
565        write_mask: u64,
566        ret_value: u64,
567    },
568}
569
570/// A device type to create with `Vm.create_device`.
571#[derive(Clone, Copy, Debug, PartialEq, Eq)]
572pub enum DeviceKind {
573    /// VFIO device for direct access to devices from userspace
574    Vfio,
575    /// ARM virtual general interrupt controller v2
576    #[cfg(target_arch = "aarch64")]
577    ArmVgicV2,
578    /// ARM virtual general interrupt controller v3
579    #[cfg(target_arch = "aarch64")]
580    ArmVgicV3,
581    /// ARM virtual interrupt translation service
582    #[cfg(target_arch = "aarch64")]
583    ArmVgicIts,
584    /// RiscV AIA in-kernel emulation
585    #[cfg(target_arch = "riscv64")]
586    RiscvAia,
587}
588
589/// The source chip of an `IrqSource`
590#[repr(C)]
591#[derive(Clone, Copy, Debug, PartialEq, Eq, Serialize, Deserialize)]
592pub enum IrqSourceChip {
593    PicPrimary,
594    PicSecondary,
595    Ioapic,
596    Gic,
597    Aia,
598}
599
600/// A source of IRQs in an `IrqRoute`.
601#[repr(C)]
602#[derive(Clone, Copy, Debug, PartialEq, Eq, Serialize, Deserialize)]
603pub enum IrqSource {
604    Irqchip {
605        chip: IrqSourceChip,
606        pin: u32,
607    },
608    Msi {
609        address: u64,
610        data: u32,
611        #[cfg(target_arch = "aarch64")]
612        pci_address: resources::PciAddress,
613    },
614}
615
616/// A single route for an IRQ.
617#[repr(C)]
618#[derive(Clone, Copy, Debug, PartialEq, Eq, Serialize, Deserialize)]
619pub struct IrqRoute {
620    pub gsi: u32,
621    pub source: IrqSource,
622}
623
624/// The state of the paravirtual clock.
625#[derive(Debug, Default, Copy, Clone, Serialize, Deserialize)]
626pub struct ClockState {
627    /// Current pv clock timestamp, as seen by the guest
628    pub clock: u64,
629}
630
631/// The MPState represents the state of a processor.
632#[repr(C)]
633#[derive(Clone, Copy, Debug, PartialEq, Eq, Serialize, Deserialize)]
634pub enum MPState {
635    /// the vcpu is currently running (x86/x86_64,arm/arm64)
636    Runnable,
637    /// the vcpu is an application processor (AP) which has not yet received an INIT signal
638    /// (x86/x86_64)
639    Uninitialized,
640    /// the vcpu has received an INIT signal, and is now ready for a SIPI (x86/x86_64)
641    InitReceived,
642    /// the vcpu has executed a HLT instruction and is waiting for an interrupt (x86/x86_64)
643    Halted,
644    /// the vcpu has just received a SIPI (vector accessible via KVM_GET_VCPU_EVENTS) (x86/x86_64)
645    SipiReceived,
646    /// the vcpu is stopped (arm/arm64)
647    Stopped,
648}
649
650/// Whether the VM should be run in protected mode or not.
651#[derive(Copy, Clone, Debug, Deserialize, Eq, PartialEq, Serialize)]
652pub enum ProtectionType {
653    /// The VM should be run in the unprotected mode, where the host has access to its memory.
654    Unprotected,
655    /// The VM should be run in protected mode, so the host cannot access its memory directly. It
656    /// should be booted via the protected VM firmware, so that it can access its secrets.
657    Protected,
658    /// The VM should be run in protected mode, so the host cannot access its memory directly. It
659    /// should be booted via a custom VM firmware, useful for debugging and testing.
660    ProtectedWithCustomFirmware,
661    /// The VM should be run in protected mode, but booted directly without pVM firmware. The host
662    /// will still be unable to access the VM memory, but it won't be given any secrets.
663    ProtectedWithoutFirmware,
664    /// The VM should be run in unprotected mode, but with the same memory layout as protected
665    /// mode, protected VM firmware loaded, and simulating protected mode as much as possible.
666    /// This is useful for debugging the protected VM firmware and other protected mode issues.
667    UnprotectedWithFirmware,
668}
669
670impl ProtectionType {
671    /// Returns whether the hypervisor will prevent us from accessing the VM's memory.
672    pub fn isolates_memory(&self) -> bool {
673        matches!(
674            self,
675            Self::Protected | Self::ProtectedWithCustomFirmware | Self::ProtectedWithoutFirmware
676        )
677    }
678
679    /// Returns whether the VMM needs to load the pVM firmware.
680    pub fn needs_firmware_loaded(&self) -> bool {
681        matches!(
682            self,
683            Self::UnprotectedWithFirmware | Self::ProtectedWithCustomFirmware
684        )
685    }
686
687    /// Returns whether the VM runs a pVM firmware.
688    pub fn runs_firmware(&self) -> bool {
689        self.needs_firmware_loaded() || matches!(self, Self::Protected)
690    }
691}
692
693#[derive(Clone, Copy)]
694pub struct Config {
695    #[cfg(target_arch = "aarch64")]
696    /// enable the Memory Tagging Extension in the guest
697    pub mte: bool,
698    pub protection_type: ProtectionType,
699    #[cfg(all(target_os = "android", target_arch = "aarch64"))]
700    pub ffa: bool,
701    pub force_disable_readonly_mem: bool,
702}
703
704impl Default for Config {
705    fn default() -> Config {
706        Config {
707            #[cfg(target_arch = "aarch64")]
708            mte: false,
709            protection_type: ProtectionType::Unprotected,
710            #[cfg(all(target_os = "android", target_arch = "aarch64"))]
711            ffa: false,
712            force_disable_readonly_mem: false,
713        }
714    }
715}