nub_host_kvm/hypervisor/virtual_machine/
mod.rs1use std::fmt::Debug;
18use std::sync::OnceLock;
19
20use tracing::{Span, instrument};
21
22#[cfg(gdb)]
23use crate::hypervisor::gdb::DebugError;
24use crate::hypervisor::regs::{
25 CommonDebugRegs, CommonFpu, CommonRegisters, CommonSpecialRegisters,
26};
27use crate::mem::memory_region::MemoryRegion;
28#[cfg(feature = "trace_guest")]
29use crate::sandbox::trace::TraceContext as SandboxTraceContext;
30
31#[cfg(kvm)]
33pub(crate) mod kvm;
34
35#[cfg(feature = "hw-interrupts")]
37pub(crate) mod x86_64;
38
39static AVAILABLE_HYPERVISOR: OnceLock<Option<HypervisorType>> = OnceLock::new();
40
41pub fn get_available_hypervisor() -> &'static Option<HypervisorType> {
43 AVAILABLE_HYPERVISOR.get_or_init(|| {
44 #[cfg(kvm)]
45 {
46 if kvm::is_hypervisor_present() {
47 Some(HypervisorType::Kvm)
48 } else {
49 None
50 }
51 }
52 #[cfg(not(kvm))]
53 {
54 None
55 }
56 })
57}
58
59#[instrument(skip_all, parent = Span::current())]
62pub fn is_hypervisor_present() -> bool {
63 get_available_hypervisor().is_some()
64}
65
66#[derive(PartialEq, Eq, Debug, Copy, Clone)]
68pub(crate) enum HypervisorType {
69 #[cfg(kvm)]
70 Kvm,
71}
72
73#[cfg(not(any(kvm, target_arch = "aarch64")))]
75compile_error!(
76 "No hypervisor type is available for the current platform. Please enable the `kvm` cargo feature."
77);
78
79pub(crate) enum VmExit {
81 #[cfg(gdb)]
83 Debug {
84 #[cfg(target_arch = "x86_64")]
85 dr6: u64,
86 #[cfg(target_arch = "x86_64")]
87 exception: u32,
88 },
89 Halt(),
91 IoOut(u16, Vec<u8>),
93 MmioRead(u64),
95 MmioWrite(u64),
97 Cancelled(),
99 Unknown(String),
101 #[cfg_attr(
103 any(target_os = "windows", feature = "hw-interrupts"),
104 expect(
105 dead_code,
106 reason = "Retry() is never constructed on Windows or with hw-interrupts (EAGAIN causes continue instead)"
107 )
108 )]
109 Retry(),
110}
111
112#[derive(Debug, Clone, thiserror::Error)]
114pub enum VmError {
115 #[error("Failed to create vm: {0}")]
116 CreateVm(#[from] CreateVmError),
117 #[cfg(gdb)]
118 #[error("Debug operation failed: {0}")]
119 Debug(#[from] DebugError),
120 #[error("Map memory operation failed: {0}")]
121 MapMemory(#[from] MapMemoryError),
122 #[error("Register operation failed: {0}")]
123 Register(#[from] RegisterError),
124 #[error("Failed to run vcpu: {0}")]
125 RunVcpu(#[from] RunVcpuError),
126 #[error("Unmap memory operation failed: {0}")]
127 UnmapMemory(#[from] UnmapMemoryError),
128}
129
130#[derive(Debug, Clone, thiserror::Error)]
132pub enum CreateVmError {
133 #[error("VCPU creation failed: {0}")]
134 CreateVcpuFd(HypervisorError),
135 #[error("VM creation failed: {0}")]
136 CreateVmFd(HypervisorError),
137 #[error("Hypervisor is not available: {0}")]
138 HypervisorNotAvailable(HypervisorError),
139 #[error("Initialize VM failed: {0}")]
140 InitializeVm(HypervisorError),
141 #[error("Set Partition Property failed: {0}")]
142 SetPartitionProperty(HypervisorError),
143 #[cfg(target_os = "windows")]
144 #[error("Surrogate process creation failed: {0}")]
145 SurrogateProcess(String),
146}
147
148#[derive(Debug, Clone, thiserror::Error)]
150pub enum RunVcpuError {
151 #[error("Failed to decode message type: {0}")]
152 DecodeIOMessage(u32),
153 #[cfg(gdb)]
154 #[error("Failed to get DR6 debug register: {0}")]
155 GetDr6(HypervisorError),
156 #[error("Increment RIP failed: {0}")]
157 IncrementRip(HypervisorError),
158 #[error("Parse GPA access info failed")]
159 ParseGpaAccessInfo,
160 #[error("Unknown error: {0}")]
161 Unknown(HypervisorError),
162}
163
164#[derive(Debug, Clone, thiserror::Error)]
166pub enum RegisterError {
167 #[error("Failed to get registers: {0}")]
168 GetRegs(HypervisorError),
169 #[error("Failed to set registers: {0}")]
170 SetRegs(HypervisorError),
171 #[error("Failed to get FPU registers: {0}")]
172 GetFpu(HypervisorError),
173 #[error("Failed to set FPU registers: {0}")]
174 SetFpu(HypervisorError),
175 #[error("Failed to get special registers: {0}")]
176 GetSregs(HypervisorError),
177 #[error("Failed to set special registers: {0}")]
178 SetSregs(HypervisorError),
179 #[error("Failed to get debug registers: {0}")]
180 GetDebugRegs(HypervisorError),
181 #[error("Failed to set debug registers: {0}")]
182 SetDebugRegs(HypervisorError),
183 #[error("Failed to get xsave: {0}")]
184 GetXsave(HypervisorError),
185 #[error("Failed to set xsave: {0}")]
186 SetXsave(HypervisorError),
187 #[error("Xsave size mismatch: expected {expected} bytes, got {actual}")]
188 XsaveSizeMismatch {
189 expected: u32,
191 actual: u32,
193 },
194 #[error("Invalid xsave alignment")]
195 InvalidXsaveAlignment,
196 #[cfg(target_os = "windows")]
197 #[error("Failed to get xsave size: {0}")]
198 GetXsaveSize(#[from] HypervisorError),
199 #[cfg(target_os = "windows")]
200 #[error("Failed to convert WHP registers: {0}")]
201 ConversionFailed(String),
202}
203
204#[derive(Debug, Clone, thiserror::Error)]
206pub enum MapMemoryError {
207 #[cfg(target_os = "windows")]
208 #[error("Address conversion failed: {0}")]
209 AddressConversion(std::num::TryFromIntError),
210 #[error("Hypervisor error: {0}")]
211 Hypervisor(HypervisorError),
212 #[cfg(target_os = "windows")]
213 #[error("Invalid memory region flags: {0}")]
214 InvalidFlags(String),
215 #[cfg(target_os = "windows")]
216 #[error("Failed to load API '{api_name}': {source}")]
217 LoadApi {
218 api_name: &'static str,
219 source: windows_result::Error,
220 },
221 #[cfg(target_os = "windows")]
222 #[error("Operation not supported: {0}")]
223 NotSupported(String),
224 #[cfg(target_os = "windows")]
225 #[error("Surrogate process creation failed: {0}")]
226 SurrogateProcess(String),
227}
228
229#[derive(Debug, Clone, thiserror::Error)]
231pub enum UnmapMemoryError {
232 #[error("Hypervisor error: {0}")]
233 Hypervisor(HypervisorError),
234}
235
236#[derive(Debug, Clone, thiserror::Error)]
238pub enum HypervisorError {
239 #[cfg(kvm)]
240 #[error("KVM error: {0}")]
241 KvmError(#[from] kvm_ioctls::Error),
242}
243
244pub(crate) trait VirtualMachine: Debug + Send {
247 unsafe fn map_memory(
256 &mut self,
257 region: (u32, &MemoryRegion),
258 ) -> std::result::Result<(), MapMemoryError>;
259
260 fn run_vcpu(
264 &mut self,
265 #[cfg(feature = "trace_guest")] tc: &mut SandboxTraceContext,
266 ) -> std::result::Result<VmExit, RunVcpuError>;
267
268 #[allow(dead_code)]
270 fn regs(&self) -> std::result::Result<CommonRegisters, RegisterError>;
271 fn set_regs(&self, regs: &CommonRegisters) -> std::result::Result<(), RegisterError>;
273 #[allow(dead_code)]
275 fn fpu(&self) -> std::result::Result<CommonFpu, RegisterError>;
276 fn set_fpu(&self, fpu: &CommonFpu) -> std::result::Result<(), RegisterError>;
278 #[allow(dead_code)]
280 fn sregs(&self) -> std::result::Result<CommonSpecialRegisters, RegisterError>;
281 fn set_sregs(&self, sregs: &CommonSpecialRegisters) -> std::result::Result<(), RegisterError>;
283 #[allow(dead_code)]
285 fn debug_regs(&self) -> std::result::Result<CommonDebugRegs, RegisterError>;
286
287 #[allow(dead_code)]
289 fn xsave(&self) -> std::result::Result<Vec<u8>, RegisterError>;
290
291 #[cfg(target_os = "windows")]
293 fn partition_handle(&self) -> windows::Win32::System::Hypervisor::WHV_PARTITION_HANDLE;
294}
295
296#[cfg(test)]
297mod tests {
298 #[test]
299 #[cfg(kvm)]
300 fn is_hypervisor_present() {
301 use std::path::Path;
302 assert_eq!(
303 Path::new("/dev/kvm").exists(),
304 super::is_hypervisor_present()
305 );
306 }
307}