Skip to main content

arcbox_hypervisor/
types.rs

1//! Common types used across the hypervisor crate.
2
3use serde::{Deserialize, Serialize};
4
5/// CPU architecture.
6#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, Serialize, Deserialize)]
7pub enum CpuArch {
8    /// `x86_64` / AMD64
9    X86_64,
10    /// ARM64 / `AArch64`
11    Aarch64,
12}
13
14impl CpuArch {
15    /// Returns the native CPU architecture of the current system.
16    #[must_use]
17    pub const fn native() -> Self {
18        #[cfg(target_arch = "x86_64")]
19        {
20            Self::X86_64
21        }
22        #[cfg(target_arch = "aarch64")]
23        {
24            Self::Aarch64
25        }
26        #[cfg(not(any(target_arch = "x86_64", target_arch = "aarch64")))]
27        {
28            compile_error!("Unsupported CPU architecture")
29        }
30    }
31}
32
33/// Platform capabilities reported by the hypervisor.
34#[derive(Debug, Clone)]
35pub struct PlatformCapabilities {
36    /// Supported CPU architectures.
37    pub supported_archs: Vec<CpuArch>,
38    /// Maximum number of vCPUs per VM.
39    pub max_vcpus: u32,
40    /// Maximum memory size in bytes.
41    pub max_memory: u64,
42    /// Whether nested virtualization is supported.
43    pub nested_virt: bool,
44    /// Whether Rosetta 2 translation is available (macOS only).
45    pub rosetta: bool,
46}
47
48impl Default for PlatformCapabilities {
49    fn default() -> Self {
50        Self {
51            supported_archs: vec![CpuArch::native()],
52            max_vcpus: 1,
53            max_memory: 1024 * 1024 * 1024, // 1GB default
54            nested_virt: false,
55            rosetta: false,
56        }
57    }
58}
59
60/// CPU register state.
61#[derive(Debug, Clone, Default, Serialize, Deserialize)]
62pub struct Registers {
63    // General purpose registers (x86_64)
64    pub rax: u64,
65    pub rbx: u64,
66    pub rcx: u64,
67    pub rdx: u64,
68    pub rsi: u64,
69    pub rdi: u64,
70    pub rsp: u64,
71    pub rbp: u64,
72    pub r8: u64,
73    pub r9: u64,
74    pub r10: u64,
75    pub r11: u64,
76    pub r12: u64,
77    pub r13: u64,
78    pub r14: u64,
79    pub r15: u64,
80
81    // Instruction pointer and flags
82    pub rip: u64,
83    pub rflags: u64,
84}
85
86/// Reason for vCPU exit.
87#[derive(Debug, Clone)]
88pub enum VcpuExit {
89    /// VM halted.
90    Halt,
91    /// I/O port access.
92    IoOut {
93        port: u16,
94        size: u8,
95        data: u64,
96    },
97    IoIn {
98        port: u16,
99        size: u8,
100    },
101    /// Memory-mapped I/O.
102    MmioRead {
103        addr: u64,
104        size: u8,
105    },
106    MmioWrite {
107        addr: u64,
108        size: u8,
109        data: u64,
110    },
111    /// Hypercall.
112    Hypercall {
113        nr: u64,
114        args: [u64; 6],
115    },
116    /// System reset requested.
117    SystemReset,
118    /// Shutdown requested.
119    Shutdown,
120    /// Debug exception.
121    Debug,
122    /// Unknown exit reason.
123    Unknown(i32),
124}
125
126/// `VirtIO` device configuration for attaching to a VM.
127#[derive(Debug, Clone, Serialize, Deserialize)]
128pub struct VirtioDeviceConfig {
129    /// Device type.
130    pub device_type: VirtioDeviceType,
131    /// Device-specific configuration.
132    pub config: Vec<u8>,
133    /// Path to device (for block/fs devices).
134    pub path: Option<String>,
135    /// Whether the device is read-only.
136    pub read_only: bool,
137    /// Tag for filesystem devices.
138    pub tag: Option<String>,
139    /// File descriptor for file-handle-based network attachment.
140    #[serde(skip)]
141    pub net_fd: Option<i32>,
142}
143
144impl VirtioDeviceConfig {
145    /// Creates a new block device configuration.
146    pub fn block(path: impl Into<String>, read_only: bool) -> Self {
147        Self {
148            device_type: VirtioDeviceType::Block,
149            config: Vec::new(),
150            path: Some(path.into()),
151            read_only,
152            tag: None,
153            net_fd: None,
154        }
155    }
156
157    /// Creates a new network device configuration with NAT attachment.
158    #[must_use]
159    pub const fn network() -> Self {
160        Self {
161            device_type: VirtioDeviceType::Net,
162            config: Vec::new(),
163            path: None,
164            read_only: false,
165            tag: None,
166            net_fd: None,
167        }
168    }
169
170    /// Creates a network device configuration with file-handle attachment.
171    ///
172    /// The VZ framework side uses one connected datagram socket file descriptor
173    /// for bidirectional frame I/O.
174    #[must_use]
175    pub const fn network_file_handle(fd: i32) -> Self {
176        Self {
177            device_type: VirtioDeviceType::Net,
178            config: Vec::new(),
179            path: None,
180            read_only: false,
181            tag: None,
182            net_fd: Some(fd),
183        }
184    }
185
186    /// Creates a new console device configuration.
187    #[must_use]
188    pub const fn console() -> Self {
189        Self {
190            device_type: VirtioDeviceType::Console,
191            config: Vec::new(),
192            path: None,
193            read_only: false,
194            tag: None,
195            net_fd: None,
196        }
197    }
198
199    /// Creates a new filesystem device configuration.
200    pub fn filesystem(path: impl Into<String>, tag: impl Into<String>, read_only: bool) -> Self {
201        Self {
202            device_type: VirtioDeviceType::Fs,
203            config: Vec::new(),
204            path: Some(path.into()),
205            read_only,
206            tag: Some(tag.into()),
207            net_fd: None,
208        }
209    }
210
211    /// Creates a new vsock device configuration.
212    #[must_use]
213    pub const fn vsock() -> Self {
214        Self {
215            device_type: VirtioDeviceType::Vsock,
216            config: Vec::new(),
217            path: None,
218            read_only: false,
219            tag: None,
220            net_fd: None,
221        }
222    }
223
224    /// Creates a new entropy device configuration.
225    #[must_use]
226    pub const fn entropy() -> Self {
227        Self {
228            device_type: VirtioDeviceType::Rng,
229            config: Vec::new(),
230            path: None,
231            read_only: false,
232            tag: None,
233            net_fd: None,
234        }
235    }
236}
237
238/// `VirtIO` device types.
239#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize)]
240pub enum VirtioDeviceType {
241    /// Block device.
242    Block,
243    /// Network device.
244    Net,
245    /// Console device.
246    Console,
247    /// Filesystem (9p/virtiofs).
248    Fs,
249    /// Socket device.
250    Vsock,
251    /// Entropy source.
252    Rng,
253    /// Balloon device.
254    Balloon,
255    /// GPU device.
256    Gpu,
257}
258
259// ============================================================================
260// Memory Balloon Types
261// ============================================================================
262
263/// Memory balloon statistics.
264#[derive(Debug, Clone, Default, Serialize, Deserialize)]
265pub struct BalloonStats {
266    /// Target memory size in bytes.
267    ///
268    /// This is the memory size the balloon is trying to achieve.
269    pub target_bytes: u64,
270
271    /// Current balloon size in bytes.
272    ///
273    /// This is how much memory the balloon has currently claimed.
274    /// `actual_guest_memory = configured_memory - current_balloon_size`
275    pub current_bytes: u64,
276
277    /// Configured VM memory size in bytes.
278    ///
279    /// This is the maximum memory available to the guest when
280    /// the balloon is fully deflated.
281    pub configured_bytes: u64,
282}
283
284impl BalloonStats {
285    /// Returns the effective memory available to the guest in bytes.
286    ///
287    /// This is `configured_bytes - current_bytes`.
288    #[must_use]
289    pub const fn effective_memory(&self) -> u64 {
290        self.configured_bytes.saturating_sub(self.current_bytes)
291    }
292
293    /// Returns the target memory as a percentage of configured memory.
294    #[must_use]
295    pub fn target_percent(&self) -> f64 {
296        if self.configured_bytes == 0 {
297            return 100.0;
298        }
299        (self.target_bytes as f64 / self.configured_bytes as f64) * 100.0
300    }
301}
302
303impl VirtioDeviceConfig {
304    /// Creates a new balloon device configuration.
305    ///
306    /// The balloon device allows dynamic memory management by inflating
307    /// (reclaiming memory from guest) or deflating (returning memory to guest).
308    #[must_use]
309    pub const fn balloon() -> Self {
310        Self {
311            device_type: VirtioDeviceType::Balloon,
312            config: Vec::new(),
313            path: None,
314            read_only: false,
315            tag: None,
316            net_fd: None,
317        }
318    }
319}
320
321// ============================================================================
322// Snapshot Types
323// ============================================================================
324
325/// ARM64 register state for snapshots.
326#[derive(Debug, Clone, Default, Serialize, Deserialize)]
327pub struct Arm64Registers {
328    /// General purpose registers X0-X30.
329    pub x: [u64; 31],
330    /// Stack pointer (SP).
331    pub sp: u64,
332    /// Program counter (PC).
333    pub pc: u64,
334    /// Processor state (PSTATE/CPSR).
335    pub pstate: u64,
336    /// Floating point control register.
337    pub fpcr: u64,
338    /// Floating point status register.
339    pub fpsr: u64,
340    /// Vector registers Q0-Q31 (128-bit each, stored as [u64; 2]).
341    pub v: [[u64; 2]; 32],
342}
343
344/// vCPU snapshot state.
345#[derive(Debug, Clone, Serialize, Deserialize)]
346pub struct VcpuSnapshot {
347    /// vCPU ID.
348    pub id: u32,
349    /// CPU architecture.
350    pub arch: CpuArch,
351    /// `x86_64` registers (if applicable).
352    pub x86_regs: Option<Registers>,
353    /// ARM64 registers (if applicable).
354    pub arm64_regs: Option<Arm64Registers>,
355    /// Additional architecture-specific state (opaque bytes).
356    pub extra_state: Vec<u8>,
357}
358
359impl VcpuSnapshot {
360    /// Creates a new `x86_64` vCPU snapshot.
361    #[must_use]
362    pub const fn new_x86(id: u32, regs: Registers) -> Self {
363        Self {
364            id,
365            arch: CpuArch::X86_64,
366            x86_regs: Some(regs),
367            arm64_regs: None,
368            extra_state: Vec::new(),
369        }
370    }
371
372    /// Creates a new ARM64 vCPU snapshot.
373    #[must_use]
374    pub const fn new_arm64(id: u32, regs: Arm64Registers) -> Self {
375        Self {
376            id,
377            arch: CpuArch::Aarch64,
378            x86_regs: None,
379            arm64_regs: Some(regs),
380            extra_state: Vec::new(),
381        }
382    }
383
384    /// Returns `true` if this snapshot contains only default (zeroed) register
385    /// state, i.e. it was created as a placeholder and does not represent real
386    /// captured vCPU registers.
387    #[must_use]
388    pub fn is_placeholder(&self) -> bool {
389        match (&self.x86_regs, &self.arm64_regs) {
390            (Some(regs), _) => regs.rip == 0 && regs.rsp == 0 && regs.rflags == 0,
391            (_, Some(regs)) => regs.pc == 0 && regs.sp == 0 && regs.pstate == 0,
392            (None, None) => true,
393        }
394    }
395}
396
397/// Device snapshot state.
398#[derive(Debug, Clone, Serialize, Deserialize)]
399pub struct DeviceSnapshot {
400    /// Device type.
401    pub device_type: VirtioDeviceType,
402    /// Device name/identifier.
403    pub name: String,
404    /// Device-specific state (serialized).
405    pub state: Vec<u8>,
406}
407
408/// Memory region info for snapshots.
409#[derive(Debug, Clone, Serialize, Deserialize)]
410pub struct MemoryRegionSnapshot {
411    /// Guest physical address start.
412    pub guest_addr: u64,
413    /// Region size in bytes.
414    pub size: u64,
415    /// Whether this region is read-only.
416    pub read_only: bool,
417    /// Offset in the memory dump file.
418    pub file_offset: u64,
419}
420
421/// Dirty page tracking info.
422#[derive(Debug, Clone)]
423pub struct DirtyPageInfo {
424    /// Guest physical address of the page.
425    pub guest_addr: u64,
426    /// Page size (usually 4KB).
427    pub size: u64,
428}
429
430/// Full VM snapshot metadata.
431#[derive(Debug, Clone, Serialize, Deserialize)]
432pub struct VmSnapshot {
433    /// Snapshot format version.
434    pub version: u32,
435    /// CPU architecture.
436    pub arch: CpuArch,
437    /// vCPU states.
438    pub vcpus: Vec<VcpuSnapshot>,
439    /// Device states.
440    pub devices: Vec<DeviceSnapshot>,
441    /// Memory region info.
442    pub memory_regions: Vec<MemoryRegionSnapshot>,
443    /// Total memory size.
444    pub total_memory: u64,
445    /// Whether memory is compressed.
446    pub compressed: bool,
447    /// Compression algorithm (if compressed).
448    pub compression: Option<String>,
449    /// Parent snapshot ID (for incremental).
450    pub parent_id: Option<String>,
451}