1use std::fmt::Debug;
5use std::sync::OnceLock;
6
7use tracing::{Span, instrument};
8
9#[cfg(gdb)]
10use crate::hypervisor::gdb::DebugError;
11use crate::hypervisor::regs::{
12 CommonDebugRegs, CommonFpu, CommonRegisters, CommonSpecialRegisters,
13};
14#[cfg(all(target_arch = "x86_64", any(mshv3, target_os = "windows")))]
15use crate::hypervisor::regs::{
16 MSR_MTRR_CAP, filterless_core_reset_candidates, hyperv_mtrr_reset_indices,
17};
18#[cfg(target_arch = "x86_64")]
19use crate::hypervisor::regs::{MsrEntry, is_resettable_msr};
20use crate::mem::memory_region::MemoryRegion;
21#[cfg(feature = "trace_guest")]
22use crate::sandbox::trace::TraceContext as SandboxTraceContext;
23
24#[cfg(hvf)]
26pub(crate) mod hvf;
27#[cfg(kvm)]
29pub(crate) mod kvm;
30#[cfg(mshv3)]
32pub(crate) mod mshv;
33#[cfg(target_os = "windows")]
35pub(crate) mod whp;
36
37#[cfg(feature = "hw-interrupts")]
39pub(crate) mod x86_64;
40
41static AVAILABLE_HYPERVISOR: OnceLock<Option<HypervisorType>> = OnceLock::new();
42
43pub fn get_available_hypervisor() -> &'static Option<HypervisorType> {
45 AVAILABLE_HYPERVISOR.get_or_init(|| {
46 cfg_if::cfg_if! {
47 if #[cfg(all(kvm, mshv3))] {
48 if mshv::is_hypervisor_present() {
52 Some(HypervisorType::Mshv)
53 } else if kvm::is_hypervisor_present() {
54 Some(HypervisorType::Kvm)
55 } else {
56 None
57 }
58 } else if #[cfg(kvm)] {
59 if kvm::is_hypervisor_present() {
60 Some(HypervisorType::Kvm)
61 } else {
62 None
63 }
64 } else if #[cfg(mshv3)] {
65 if mshv::is_hypervisor_present() {
66 Some(HypervisorType::Mshv)
67 } else {
68 None
69 }
70 } else if #[cfg(target_os = "windows")] {
71 if whp::is_hypervisor_present() {
72 Some(HypervisorType::Whp)
73 } else {
74 None
75 }
76 } else if #[cfg(hvf)] {
77 if hvf::is_hypervisor_present() {
78 Some(HypervisorType::Hvf)
79 } else {
80 None
81 }
82 } else {
83 None
84 }
85 }
86 })
87}
88
89#[instrument(skip_all, parent = Span::current())]
92pub fn is_hypervisor_present() -> bool {
93 get_available_hypervisor().is_some()
94}
95
96#[derive(PartialEq, Eq, Debug, Copy, Clone)]
98pub(crate) enum HypervisorType {
99 #[cfg(kvm)]
100 Kvm,
101
102 #[cfg(mshv3)]
103 Mshv,
104
105 #[cfg(target_os = "windows")]
106 Whp,
107
108 #[cfg(hvf)]
109 Hvf,
110}
111
112#[cfg(all(target_arch = "x86_64", any(mshv3, target_os = "windows")))]
116pub(crate) const XSAVE_MIN_SIZE: usize = 576;
117
118#[cfg(all(any(kvm, mshv3), test, not(target_arch = "aarch64")))]
121pub(crate) const XSAVE_BUFFER_SIZE: usize = 4096;
122
123#[cfg(target_arch = "x86_64")]
125pub(crate) const XCR0_RESET: u64 = 1;
126
127#[cfg(not(any(kvm, mshv3, target_os = "windows", target_arch = "aarch64")))]
129compile_error!(
130 "No hypervisor type is available for the current platform. Please enable either the `kvm` or `mshv3` cargo feature."
131);
132
133#[cfg_attr(target_os = "macos", allow(unused))]
135pub(crate) enum VmExit {
136 #[cfg(gdb)]
138 Debug {
139 #[cfg(target_arch = "x86_64")]
140 dr6: u64,
141 #[cfg(target_arch = "x86_64")]
142 exception: u32,
143 },
144 Halt(),
146 IoOut(u16, Vec<u8>),
148 MmioRead(u64),
150 MmioWrite(u64),
152 Cancelled(),
154 Unknown(String),
156 #[cfg_attr(
158 any(target_os = "windows", feature = "hw-interrupts"),
159 expect(
160 dead_code,
161 reason = "Retry() is never constructed on Windows or with hw-interrupts (EAGAIN causes continue instead)"
162 )
163 )]
164 Retry(),
165}
166
167#[derive(Debug, thiserror::Error)]
169pub enum VmError {
170 #[error("Failed to create vm: {0}")]
171 CreateVm(#[from] CreateVmError),
172 #[cfg(gdb)]
173 #[error("Debug operation failed: {0}")]
174 Debug(#[from] DebugError),
175 #[error("Map memory operation failed: {0}")]
176 MapMemory(#[from] MapMemoryError),
177 #[error("Register operation failed: {0}")]
178 Register(#[from] RegisterError),
179 #[error("Failed to run vcpu: {0}")]
180 RunVcpu(#[from] RunVcpuError),
181 #[error("Unmap memory operation failed: {0}")]
182 UnmapMemory(#[from] UnmapMemoryError),
183}
184
185#[derive(Debug, Clone, thiserror::Error)]
187pub enum CreateVmError {
188 #[error("VCPU creation failed: {0}")]
189 CreateVcpuFd(HypervisorError),
190 #[error("VM creation failed: {0}")]
191 CreateVmFd(HypervisorError),
192 #[error("Hypervisor is not available: {0}")]
193 HypervisorNotAvailable(HypervisorError),
194 #[error("Initialize VM failed: {0}")]
195 InitializeVm(HypervisorError),
196 #[cfg(all(kvm, target_arch = "x86_64"))]
197 #[error("KVM MSR filtering requires KVM_CAP_X86_MSR_FILTER")]
198 MsrFilterNotSupported,
199 #[cfg(target_arch = "x86_64")]
200 #[error("MSR {msr:#x} cannot be declared as a guest MSR: {reason}")]
201 MsrNotDeclarable { msr: u32, reason: String },
202 #[cfg(target_arch = "x86_64")]
203 #[error("Failed to read IA32_MTRRCAP: {0}")]
204 GetMtrrCap(RegisterError),
205 #[cfg(target_arch = "x86_64")]
206 #[error("Guest-visible MTRRs cannot be reset: {0}")]
207 RequiredMtrrsNotResettable(RegisterError),
208 #[cfg(all(target_arch = "x86_64", any(mshv3, target_os = "windows")))]
209 #[error("Core reset MSR {msr:#x} is readable but not writable on this host")]
210 MsrNotResettable { msr: u32 },
211 #[cfg(target_arch = "x86_64")]
212 #[error("Guest exposes {advertised} variable MTRR pairs, expected at most {maximum}")]
213 UnexpectedVariableMtrrCount { advertised: u8, maximum: u8 },
214 #[cfg(all(kvm, target_arch = "x86_64"))]
215 #[error("Too many guest MSR filter ranges: {0}. Maximum is 16")]
216 TooManyMsrRanges(usize),
217 #[cfg(target_os = "windows")]
218 #[error("Get Partition Property failed: {0}")]
219 GetPartitionProperty(HypervisorError),
220 #[cfg(target_os = "windows")]
221 #[error("WHP exposes {advertised} processor feature banks, expected {expected}")]
222 UnexpectedProcessorFeatureBankCount { advertised: u32, expected: u32 },
223 #[error("Set Partition Property failed: {0}")]
224 SetPartitionProperty(HypervisorError),
225 #[cfg(target_os = "windows")]
226 #[error("Surrogate process creation failed: {0}")]
227 SurrogateProcess(String),
228}
229
230#[derive(Debug, thiserror::Error)]
232pub enum RunVcpuError {
233 #[error("Failed to decode message type: {0}")]
234 DecodeIOMessage(u32),
235 #[cfg(gdb)]
236 #[error("Failed to get DR6 debug register: {0}")]
237 GetDr6(HypervisorError),
238 #[error("Increment RIP failed: {0}")]
239 IncrementRip(HypervisorError),
240 #[error("Parse GPA access info failed")]
241 ParseGpaAccessInfo,
242 #[cfg(target_arch = "aarch64")]
243 #[error("Flush MMIO pending state failed: {0}")]
244 FlushMmioPending(String),
245 #[cfg(hvf)]
246 #[error("HVF sync error: {0}")]
247 HvfSync(HvfSyncError),
248 #[error("Unknown error: {0}")]
249 Unknown(HypervisorError),
250}
251
252#[derive(Debug, Clone, thiserror::Error)]
254pub enum RegisterError {
255 #[error("Failed to get registers: {0}")]
256 GetRegs(HypervisorError),
257 #[error("Failed to set registers: {0}")]
258 SetRegs(HypervisorError),
259 #[error("Failed to get FPU registers: {0}")]
260 GetFpu(HypervisorError),
261 #[error("Failed to set FPU registers: {0}")]
262 SetFpu(HypervisorError),
263 #[error("Failed to get special registers: {0}")]
264 GetSregs(HypervisorError),
265 #[error("Failed to set special registers: {0}")]
266 SetSregs(HypervisorError),
267 #[cfg(target_arch = "x86_64")]
268 #[error("Snapshot APIC_BASE {value:#x} enables unsupported x2APIC mode")]
269 InvalidSnapshotApicBase {
270 value: u64,
272 },
273 #[error("Failed to get debug registers: {0}")]
274 GetDebugRegs(HypervisorError),
275 #[error("Failed to set debug registers: {0}")]
276 SetDebugRegs(HypervisorError),
277 #[error("Failed to get xsave: {0}")]
278 GetXsave(HypervisorError),
279 #[error("Failed to set xsave: {0}")]
280 SetXsave(HypervisorError),
281 #[cfg(target_arch = "x86_64")]
282 #[error("Failed to get XCRs: {0}")]
283 GetXcrs(HypervisorError),
284 #[cfg(target_arch = "x86_64")]
285 #[error("Failed to set XCRs: {0}")]
286 SetXcrs(HypervisorError),
287 #[cfg(target_arch = "x86_64")]
288 #[error("Hypervisor did not return XCR0")]
289 MissingXcr0,
290 #[error("Xsave size mismatch: expected {expected} bytes, got {actual}")]
291 XsaveSizeMismatch {
292 expected: u32,
294 actual: u32,
296 },
297 #[error("Invalid xsave alignment")]
298 InvalidXsaveAlignment,
299 #[cfg(target_arch = "x86_64")]
300 #[error("MSR operation not supported on this hypervisor")]
301 MsrsUnsupported,
302 #[cfg(target_arch = "x86_64")]
303 #[error("Failed to build MSR list: {0}")]
304 MsrBuild(String),
305 #[cfg(target_arch = "x86_64")]
306 #[error("Failed to get MSRs: {0}")]
307 GetMsrs(HypervisorError),
308 #[cfg(target_arch = "x86_64")]
309 #[error("Failed to set MSRs: {0}")]
310 SetMsrs(HypervisorError),
311 #[cfg(target_arch = "x86_64")]
312 #[error("Failed to set batched registers: {0}")]
313 SetBatchedRegisters(HypervisorError),
314 #[cfg(target_arch = "x86_64")]
315 #[error("Batched register writes are not supported")]
316 BatchedSetRegistersUnsupported,
317 #[cfg(target_arch = "x86_64")]
318 #[error("Snapshot MSR index {index:#x} is not in this VM's reset set")]
319 InvalidSnapshotMsrIndex {
320 index: u32,
322 },
323 #[cfg(all(kvm, target_arch = "x86_64"))]
324 #[error("MSR batch short count: expected {expected}, applied {actual}")]
325 MsrShortCount {
326 expected: usize,
328 actual: usize,
330 },
331 #[cfg(target_os = "windows")]
332 #[error("Failed to get xsave size: {0}")]
333 GetXsaveSize(#[from] HypervisorError),
334 #[cfg(target_os = "windows")]
335 #[error("Failed to convert WHP registers: {0}")]
336 ConversionFailed(String),
337}
338
339#[derive(Debug, thiserror::Error)]
340pub enum ResetVcpuError {
341 #[error("Single-operation vcpu reset not supported on this hypervisor")]
342 NotSupported,
343 #[error("Hypervisor operation failed: {0}")]
344 Hypervisor(HypervisorError),
345 #[error("Register operation failed: {0}")]
346 Register(#[from] RegisterError),
347 #[error("Operation failed: {0}")]
348 Unknown(String),
349}
350
351#[derive(Debug, thiserror::Error)]
353pub enum MapMemoryError {
354 #[cfg(target_os = "windows")]
355 #[error("Address conversion failed: {0}")]
356 AddressConversion(std::num::TryFromIntError),
357 #[error("Hypervisor error: {0}")]
358 Hypervisor(HypervisorError),
359 #[cfg(target_os = "windows")]
360 #[error("Invalid memory region flags: {0}")]
361 InvalidFlags(String),
362 #[cfg(target_os = "windows")]
363 #[error("Failed to load API '{api_name}': {source}")]
364 LoadApi {
365 api_name: &'static str,
366 source: windows_result::Error,
367 },
368 #[cfg(target_os = "windows")]
369 #[error("Operation not supported: {0}")]
370 NotSupported(String),
371 #[cfg(target_os = "windows")]
372 #[error("Surrogate process creation failed: {0}")]
373 SurrogateProcess(String),
374}
375
376#[derive(Debug, thiserror::Error)]
378pub enum UnmapMemoryError {
379 #[error("Hypervisor error: {0}")]
380 Hypervisor(HypervisorError),
381}
382
383#[derive(Debug, Clone, thiserror::Error)]
385pub enum HypervisorError {
386 #[cfg(test)]
387 #[error("Injected hypervisor error")]
388 Injected,
389 #[cfg(kvm)]
390 #[error("KVM error: {0}")]
391 KvmError(#[from] kvm_ioctls::Error),
392 #[cfg(mshv3)]
393 #[error("MSHV error: {0}")]
394 MshvError(#[from] mshv_ioctls::MshvError),
395 #[cfg(target_os = "windows")]
396 #[error("Windows error: {0}")]
397 WindowsError(#[from] windows_result::Error),
398 #[cfg(hvf)]
399 #[error("HVF error: {0}")]
400 HvfError(hvf::bindings::hv_return_t),
401}
402
403#[cfg(hvf)]
405#[derive(Debug, thiserror::Error)]
406pub enum MemorySpaceInstallError {
407 #[error("Failed to update VM/VCPU state: {0}")]
408 Hypervisor(#[from] HypervisorError),
409 #[error("Unexpected VCPU exit: {0:?}")]
410 UnexpectedExit(hvf::bindings::hv_vcpu_exit_t),
411 #[error("Failed to allocate ReadonlySharedMemory: {0}")]
412 SharedMemoryCreation(#[from] crate::mem::shared_mem::SharedMemoryError),
413}
414#[cfg(hvf)]
415#[derive(Debug, thiserror::Error)]
416pub enum HvfSyncError {
417 #[error("Error creating VCPU: {0}")]
418 CreateVcpu(HypervisorError),
419 #[error("Error resetting VCPU: {0}")]
420 ResetVcpu(HypervisorError),
421 #[error("Error reading/writing registers: {0}")]
422 Register(#[from] RegisterError),
423 #[error("Error updating memory space: {0}")]
424 MemorySpace(#[from] MemorySpaceInstallError),
425 #[error("Invariant violation: vcpu in unexpected sync state: {0}")]
426 SyncInvariant(String),
427}
428
429pub(crate) trait VirtualMachine: Debug + Send {
432 unsafe fn map_memory(
441 &mut self,
442 region: (u32, &MemoryRegion),
443 ) -> std::result::Result<(), MapMemoryError>;
444
445 fn unmap_memory(
447 &mut self,
448 region: (u32, &MemoryRegion),
449 ) -> std::result::Result<(), UnmapMemoryError>;
450
451 fn run_vcpu(
455 &mut self,
456 #[cfg(feature = "trace_guest")] tc: &mut SandboxTraceContext,
457 ) -> std::result::Result<VmExit, RunVcpuError>;
458
459 #[allow(dead_code)]
461 fn regs(&self) -> std::result::Result<CommonRegisters, RegisterError>;
462 fn set_regs(&mut self, regs: &CommonRegisters) -> std::result::Result<(), RegisterError>;
464 #[allow(dead_code)]
466 fn fpu(&self) -> std::result::Result<CommonFpu, RegisterError>;
467 fn set_fpu(&mut self, fpu: &CommonFpu) -> std::result::Result<(), RegisterError>;
469 #[allow(dead_code)]
471 fn sregs(&self) -> std::result::Result<CommonSpecialRegisters, RegisterError>;
472 fn set_sregs(
474 &mut self,
475 sregs: &CommonSpecialRegisters,
476 ) -> std::result::Result<(), RegisterError>;
477 #[allow(dead_code)]
479 fn debug_regs(&self) -> std::result::Result<CommonDebugRegs, RegisterError>;
480 #[allow(dead_code)]
482 fn set_debug_regs(&self, drs: &CommonDebugRegs) -> std::result::Result<(), RegisterError>;
483
484 #[cfg(target_arch = "x86_64")]
486 fn msrs(&self, indices: &[u32]) -> std::result::Result<Vec<MsrEntry>, RegisterError>;
487 #[cfg(target_arch = "x86_64")]
489 fn set_msrs(&self, msrs: &[MsrEntry]) -> std::result::Result<(), RegisterError>;
490 #[cfg(target_arch = "x86_64")]
492 fn msr_reset_indices(&self, guest_msrs: &[u32])
493 -> std::result::Result<Vec<u32>, CreateVmError>;
494
495 #[allow(dead_code)]
497 #[cfg(not(target_arch = "aarch64"))]
498 fn xsave(&self) -> std::result::Result<Vec<u8>, RegisterError>;
499 #[cfg(not(target_arch = "aarch64"))]
501 fn reset_xsave(&self) -> std::result::Result<(), RegisterError>;
502 #[cfg(test)]
504 #[cfg(not(target_arch = "aarch64"))]
505 fn set_xsave(&self, xsave: &[u32]) -> std::result::Result<(), RegisterError>;
506
507 #[cfg(all(test, target_arch = "x86_64"))]
508 fn xcr0(&self) -> std::result::Result<u64, RegisterError>;
509 #[cfg(target_arch = "x86_64")]
510 fn set_xcr0(&self, value: u64) -> std::result::Result<(), RegisterError>;
511
512 #[cfg(target_arch = "x86_64")]
513 fn can_batch_registers(&self) -> bool {
514 false
515 }
516 #[cfg(target_arch = "x86_64")]
517 fn set_batched_registers(
518 &mut self,
519 _regs: &CommonRegisters,
520 _debug_regs: &CommonDebugRegs,
521 _sregs: &CommonSpecialRegisters,
522 _xcr0: u64,
523 _msrs: &[MsrEntry],
524 ) -> std::result::Result<(), RegisterError> {
525 Err(RegisterError::BatchedSetRegistersUnsupported)
526 }
527
528 #[cfg(target_arch = "aarch64")]
530 fn can_reset_vcpu(&self) -> bool {
531 false
532 }
533 #[cfg(target_arch = "aarch64")]
534 fn reset_vcpu(&mut self) -> std::result::Result<(), ResetVcpuError> {
535 Err(ResetVcpuError::NotSupported)
536 }
537 #[cfg(target_os = "windows")]
539 fn partition_handle(&self) -> windows::Win32::System::Hypervisor::WHV_PARTITION_HANDLE;
540}
541
542#[cfg(target_arch = "x86_64")]
544enum MsrProbe {
545 Unreadable,
547 Unwritable,
549}
550
551#[cfg(target_arch = "x86_64")]
554fn probe_resettable(vm: &dyn VirtualMachine, msr: u32) -> Result<(), MsrProbe> {
555 let captured = vm.msrs(&[msr]).map_err(|_| MsrProbe::Unreadable)?;
556 vm.set_msrs(&captured).map_err(|_| MsrProbe::Unwritable)
557}
558
559#[cfg(target_arch = "x86_64")]
563pub(crate) fn validate_guest_msrs(
564 vm: &dyn VirtualMachine,
565 guest_msrs: &[u32],
566) -> std::result::Result<(), CreateVmError> {
567 for &msr in guest_msrs {
568 if !is_resettable_msr(msr) {
569 return Err(CreateVmError::MsrNotDeclarable {
570 msr,
571 reason: "MSR is not a resettable MSR".to_string(),
572 });
573 }
574 probe_resettable(vm, msr).map_err(|probe| CreateVmError::MsrNotDeclarable {
576 msr,
577 reason: match probe {
578 MsrProbe::Unreadable => "MSR cannot be read on this host",
579 MsrProbe::Unwritable => "MSR cannot be written on this host",
580 }
581 .to_string(),
582 })?;
583 }
584 Ok(())
585}
586
587#[cfg(all(target_arch = "x86_64", any(mshv3, target_os = "windows")))]
589pub(crate) fn mtrr_reset_indices(
590 vm: &dyn VirtualMachine,
591) -> std::result::Result<Vec<u32>, CreateVmError> {
592 let mtrr_cap = vm
593 .msrs(&[MSR_MTRR_CAP])
594 .map_err(CreateVmError::GetMtrrCap)?[0]
595 .value;
596 let indices = hyperv_mtrr_reset_indices(mtrr_cap)?;
597 vm.msrs(&indices)
598 .map_err(CreateVmError::RequiredMtrrsNotResettable)?;
599 Ok(indices)
600}
601
602#[cfg(all(target_arch = "x86_64", any(mshv3, target_os = "windows")))]
604pub(crate) fn hyperv_msr_reset_indices(
605 vm: &dyn VirtualMachine,
606 guest_msrs: &[u32],
607) -> std::result::Result<Vec<u32>, CreateVmError> {
608 static DEFAULT: OnceLock<Vec<u32>> = OnceLock::new();
611
612 if guest_msrs.is_empty()
614 && let Some(indices) = DEFAULT.get()
615 {
616 return Ok(indices.clone());
617 }
618
619 validate_guest_msrs(vm, guest_msrs)?;
620 let mut indices = filterless_core_reset_candidates()
621 .filter_map(|index| match probe_resettable(vm, index) {
622 Ok(()) => Some(Ok(index)),
624 Err(MsrProbe::Unreadable) => None,
626 Err(MsrProbe::Unwritable) => Some(Err(CreateVmError::MsrNotResettable { msr: index })),
628 })
629 .collect::<Result<Vec<u32>, _>>()?;
630 indices.extend(mtrr_reset_indices(vm)?);
634 indices.extend(guest_msrs.iter().copied());
636 indices.sort_unstable();
637 indices.dedup();
638 if guest_msrs.is_empty() {
640 let _ = DEFAULT.set(indices.clone());
642 }
643 Ok(indices)
644}
645
646#[cfg(test)]
647mod tests {
648
649 #[test]
650 #[cfg(target_os = "linux")]
652 fn is_hypervisor_present() {
653 use std::path::Path;
654
655 cfg_if::cfg_if! {
656 if #[cfg(all(kvm, mshv3))] {
657 assert_eq!(Path::new("/dev/kvm").exists() || Path::new("/dev/mshv").exists(), super::is_hypervisor_present());
658 } else if #[cfg(kvm)] {
659 assert_eq!(Path::new("/dev/kvm").exists(), super::is_hypervisor_present());
660 } else if #[cfg(mshv3)] {
661 assert_eq!(Path::new("/dev/mshv").exists(), super::is_hypervisor_present());
662 } else {
663 assert!(!super::is_hypervisor_present());
664 }
665 }
666 }
667}