Skip to main content

squib_core/
backend.rs

1//! The hypervisor backend trait surface.
2//!
3//! Per [99-key-decisions.md § D1](../../../specs/99-key-decisions.md#d1-hvf-only-no-vz)
4//! squib ships exactly one production backend: HVF on Apple Silicon. The trait stays
5//! generic so the in-process [`BackendKind::Mock`] can stand in for unit tests without
6//! a live hypervisor.
7
8use crate::{
9    error::Result,
10    memory::{GuestMemoryRegion, GuestRange, Protection},
11    vcpu::Vcpu,
12};
13
14/// Hard upper bound on `vcpu_count`, matching upstream Firecracker's `MAX_SUPPORTED_VCPUS`.
15///
16/// See `99-key-decisions.md` § D19. A launcher that asks for `vcpu_count > 32` succeeds
17/// against squib but fails against upstream Firecracker — that is a wire deviation we
18/// explicitly reject. Validation in the API layer caps requests at the minimum of
19/// `MAX_SUPPORTED_VCPUS`, `host_physical_cores`, and `hv_vm_get_max_vcpu_count()`.
20pub const MAX_SUPPORTED_VCPUS: u32 = 32;
21
22/// Identifies the concrete backend implementation reporting [`BackendCapabilities`].
23///
24/// Squib ships exactly one production backend (HVF on Apple Silicon). The `Mock` variant
25/// exists so the trait surface can be unit-tested without a live hypervisor. The enum is
26/// `#[non_exhaustive]` to leave room for a future Apple replacement API without breaking
27/// downstream callers.
28#[derive(Debug, Clone, Copy, Eq, PartialEq)]
29#[non_exhaustive]
30pub enum BackendKind {
31    /// Apple Hypervisor.framework on Apple Silicon — squib's only production backend.
32    Hvf,
33    /// In-process mock backend used for tests; never selectable from the CLI.
34    Mock,
35}
36
37/// What a backend can and cannot do, surfaced to the VMM at construction time.
38///
39/// The VMM consults this struct to decide whether to reject a configuration up-front
40/// (e.g. `track_dirty_pages: true` on the VZ backend) or to apply a documented fallback.
41#[derive(Debug, Clone, Copy, Eq, PartialEq)]
42#[non_exhaustive]
43#[allow(clippy::struct_excessive_bools)] // each bool is an independent capability flag
44pub struct BackendCapabilities {
45    /// Which backend produced these capabilities.
46    pub kind: BackendKind,
47    /// Maximum number of vCPUs this backend will allow per VM.
48    pub max_vcpus: u32,
49    /// `true` if the backend can produce a per-page dirty bitmap.
50    pub dirty_page_tracking: bool,
51    /// `true` if the backend can perform a postcopy / on-demand-paging restore.
52    pub postcopy_restore: bool,
53    /// `true` if the backend exposes vCPU exits to userspace (HVF/KVM-style); `false` if
54    /// the device model is hidden inside the framework (VZ-style).
55    pub exposes_vcpu_exits: bool,
56    /// `true` if the backend supports custom virtio-MMIO devices.
57    pub custom_mmio_devices: bool,
58}
59
60/// A live virtual machine handle owned by a [`HypervisorBackend`].
61///
62/// The trait is split from `HypervisorBackend` because a backend may host multiple VMs
63/// (in principle) and because vCPU lifetimes are scoped to a single `Vm`.
64pub trait Vm: Send + Sync {
65    /// Map a region of guest-physical memory.
66    fn map_memory(&self, region: &GuestMemoryRegion) -> Result<()>;
67
68    /// Unmap a previously-mapped region.
69    fn unmap_memory(&self, region: &GuestMemoryRegion) -> Result<()>;
70
71    /// Change the protection of an already-mapped range. Used for software dirty-page
72    /// tracking on backends without a native dirty bitmap.
73    fn protect_memory(&self, range: GuestRange, prot: Protection) -> Result<()>;
74
75    /// Create a vCPU with the given index.
76    ///
77    /// The returned `Vcpu` must be driven from a dedicated OS thread (HVF rule); the
78    /// backend is permitted to enforce that by panicking on misuse, but callers should
79    /// never depend on that.
80    fn create_vcpu(&self, index: u32) -> Result<Box<dyn Vcpu>>;
81}
82
83/// The top-level entry point: a backend that can create VMs.
84pub trait HypervisorBackend: Send + Sync {
85    /// The concrete VM type this backend produces.
86    type Vm: Vm;
87
88    /// Returns the capabilities advertised by this backend on the current host.
89    fn capabilities(&self) -> BackendCapabilities;
90
91    /// Construct a new VM with `vcpu_count` vCPUs and `mem_size_mib` MiB of guest memory.
92    ///
93    /// Memory is not mapped at this point — the caller drives [`Vm::map_memory`] for each
94    /// region. This split keeps the trait surface compatible with both KVM-style
95    /// "register a slot" and HVF/VZ-style "hand the framework an mmap" backends.
96    fn create_vm(&self, vcpu_count: u32, mem_size_mib: u64) -> Result<Self::Vm>;
97}
98
99#[cfg(test)]
100mod tests {
101    use super::*;
102
103    #[test]
104    fn capabilities_struct_constructs() {
105        let caps = BackendCapabilities {
106            kind: BackendKind::Mock,
107            max_vcpus: 4,
108            dirty_page_tracking: false,
109            postcopy_restore: false,
110            exposes_vcpu_exits: true,
111            custom_mmio_devices: true,
112        };
113        assert_eq!(caps.max_vcpus, 4);
114    }
115}