Skip to main content

squib_core/
exit.rs

1//! The portable [`VmExit`] enum.
2//!
3//! This subsumes the union of KVM's `VcpuExit` variants and the exit shapes returned by
4//! macOS Hypervisor.framework / Virtualization.framework. Each backend decodes its own
5//! native exit struct into one of these variants; the VMM dispatch loop in `squib-vmm`
6//! is therefore backend-agnostic.
7
8use smallvec::SmallVec;
9
10/// A bounded vector of bytes carrying an MMIO/PIO payload — fits in one cache line for the
11/// common 1–8 byte access patterns.
12pub type ExitData = SmallVec<[u8; 8]>;
13
14/// The reason a vCPU returned from its run loop.
15#[derive(Debug, Clone)]
16#[non_exhaustive]
17pub enum VmExit {
18    /// Memory-mapped I/O access. The MMIO bus router in `squib-vmm` dispatches to the
19    /// appropriate device emulation.
20    Mmio {
21        /// The guest-physical address being accessed.
22        addr: u64,
23        /// `true` for a write, `false` for a read.
24        write: bool,
25        /// Payload bytes (read: filled by the device; write: the bytes the guest wrote).
26        data: ExitData,
27    },
28
29    /// Port-mapped I/O access. x86-only; on other architectures this variant never appears.
30    Pio {
31        /// The I/O port number.
32        port: u16,
33        /// `true` for `OUT`, `false` for `IN`.
34        write: bool,
35        /// Payload bytes (1, 2, or 4 wide).
36        data: ExitData,
37    },
38
39    /// The guest issued a hypercall (HVC on aarch64, VMCALL on x86).
40    Hypercall {
41        /// Hypercall function number.
42        func: u64,
43        /// Up to six argument registers from the calling convention.
44        args: [u64; 6],
45    },
46
47    /// The guest executed `WFI` (wait-for-interrupt). The vCPU should sleep until an
48    /// interrupt is injected.
49    Wfi,
50    /// The guest executed `WFE` (wait-for-event). x86's `HLT` maps to [`VmExit::Hlt`] instead.
51    Wfe,
52    /// The guest executed `HLT` (x86). Equivalent to WFI in practice.
53    Hlt,
54    /// The guest issued a system reset.
55    Reset,
56    /// The guest powered down.
57    Shutdown,
58
59    /// A debug exception (single-step, breakpoint). Surfaced when the optional `gdb` feature
60    /// is wired up; otherwise the backend treats this as an internal error.
61    Debug(DebugInfo),
62
63    /// The hypervisor reported an internal failure that does not fit any of the above
64    /// variants. The string is for human consumption only; programmatic consumers should
65    /// treat this as fatal.
66    InternalError(String),
67}
68
69/// Information surfaced on a debug exit.
70#[derive(Debug, Clone)]
71#[non_exhaustive]
72pub struct DebugInfo {
73    /// Program counter at the point of the exception.
74    pub pc: u64,
75    /// Encoded exception syndrome (architecture-specific bit layout).
76    pub syndrome: u64,
77}
78
79#[cfg(test)]
80mod tests {
81    use super::*;
82
83    #[test]
84    fn mmio_payload_inlines_for_small_widths() {
85        let data: ExitData = SmallVec::from_slice(&[0xAB, 0xCD]);
86        assert!(!data.spilled());
87    }
88
89    #[test]
90    fn variants_are_clone() {
91        let exit = VmExit::Wfi;
92        let _again = exit.clone();
93    }
94}