hyperlight_host/hypervisor/
mod.rs1#[cfg(gdb)]
6pub(crate) mod gdb;
7
8pub(crate) mod regs;
10
11pub(crate) mod virtual_machine;
12
13#[cfg(target_os = "windows")]
14pub(crate) mod surrogate_process;
16#[cfg(target_os = "windows")]
17pub(crate) mod surrogate_process_manager;
19#[cfg(target_os = "windows")]
21pub mod wrappers;
22
23#[cfg(crashdump)]
24pub(crate) mod crashdump;
25
26pub(crate) mod hyperlight_vm;
27
28use std::fmt::Debug;
29#[cfg(any(kvm, mshv3))]
30use std::sync::atomic::{AtomicBool, AtomicU64};
31use std::sync::atomic::{AtomicU8, Ordering};
32#[cfg(any(kvm, mshv3, hvf))]
33use std::time::Duration;
34
35#[derive(Debug)]
36pub(crate) struct InterruptHandleStateMachine(AtomicU8);
37impl InterruptHandleStateMachine {
38 const RUNNING_BIT: u8 = 1 << 1;
39 const CANCEL_BIT: u8 = 1 << 0;
40 #[cfg(gdb)]
41 const DEBUG_INTERRUPT_BIT: u8 = 1 << 2;
42
43 fn new() -> Self {
44 Self(AtomicU8::new(0))
45 }
46
47 pub(crate) fn set_running(&self) {
49 self.0.fetch_or(Self::RUNNING_BIT, Ordering::Release);
53 }
54
55 pub(crate) fn clear_running(&self) {
57 self.0.fetch_and(!Self::RUNNING_BIT, Ordering::Release);
59 }
60
61 pub(crate) fn is_cancelled(&self) -> bool {
63 self.get_running_cancel_debug().1
64 }
65
66 fn set_cancel(&self) {
68 self.0.fetch_or(Self::CANCEL_BIT, Ordering::Release);
71 }
72
73 fn clear_cancel(&self) {
75 self.0.fetch_and(!Self::CANCEL_BIT, Ordering::Release);
79 }
80
81 pub(crate) fn is_debug_interrupted(&self) -> bool {
83 #[cfg(gdb)]
84 {
85 self.get_running_cancel_debug().2
86 }
87 #[cfg(not(gdb))]
88 {
89 false
90 }
91 }
92
93 #[cfg(gdb)]
95 fn set_debug_interrupt(&self) {
96 self.0
97 .fetch_or(Self::DEBUG_INTERRUPT_BIT, Ordering::Release);
98 }
99
100 #[cfg(gdb)]
102 fn clear_debug_interrupt(&self) {
103 self.0
104 .fetch_and(!Self::DEBUG_INTERRUPT_BIT, Ordering::Release);
105 }
106
107 fn get_running_cancel_debug(&self) -> (bool, bool, bool) {
109 let state = self.0.load(Ordering::Acquire);
110 let running = state & Self::RUNNING_BIT != 0;
111 let cancel = state & Self::CANCEL_BIT != 0;
112 #[cfg(gdb)]
113 let debug = state & Self::DEBUG_INTERRUPT_BIT != 0;
114 #[cfg(not(gdb))]
115 let debug = false;
116 (running, cancel, debug)
117 }
118}
119
120pub(crate) trait InterruptHandleImpl: InterruptHandle {
122 #[cfg(any(kvm, mshv3))]
124 fn set_tid(&self);
125
126 #[cfg(hvf)]
128 fn set_vcpu(&self, vcpu: Option<hv_vcpu_t>);
129
130 fn set_dropped(&self);
132}
133
134pub(crate) trait InterruptHandleInternal {
135 fn state(&self) -> &InterruptHandleStateMachine;
138 fn common_kill(&self) -> bool;
141}
142
143#[allow(private_bounds)]
145pub trait InterruptHandle: Send + Sync + Debug + InterruptHandleInternal {
146 fn kill(&self) -> bool {
154 self.state().set_cancel();
155 self.common_kill()
156 }
157
158 #[cfg(gdb)]
168 fn kill_from_debugger(&self) -> bool {
169 self.state().set_debug_interrupt();
170 self.common_kill()
171 }
172
173 fn dropped(&self) -> bool;
175}
176
177#[cfg(any(kvm, mshv3, hvf))]
178#[derive(Debug)]
179pub(super) struct RetryingInterruptHandle<T: InterruptHandleImpl> {
180 retry_delay: Duration,
181 inner: T,
182}
183
184#[cfg(any(kvm, mshv3, hvf))]
185impl<T: InterruptHandleImpl> InterruptHandleImpl for RetryingInterruptHandle<T> {
186 #[cfg(any(kvm, mshv3))]
187 fn set_tid(&self) {
188 self.inner.set_tid();
189 }
190
191 #[cfg(hvf)]
192 fn set_vcpu(&self, vcpu: Option<hv_vcpu_t>) {
193 self.inner.set_vcpu(vcpu);
194 }
195
196 fn set_dropped(&self) {
197 self.inner.set_dropped();
198 }
199}
200#[cfg(any(kvm, mshv3, hvf))]
201impl<T: InterruptHandleImpl> InterruptHandle for RetryingInterruptHandle<T> {
202 fn dropped(&self) -> bool {
203 self.inner.dropped()
204 }
205}
206#[cfg(any(kvm, mshv3, hvf))]
207impl<T: InterruptHandleImpl> InterruptHandleInternal for RetryingInterruptHandle<T> {
208 fn state(&self) -> &InterruptHandleStateMachine {
209 self.inner.state()
210 }
211 fn common_kill(&self) -> bool {
212 let mut succeeded = false;
213 loop {
214 let (running, cancel, debug) = self.state().get_running_cancel_debug();
215 let should_continue = running && (cancel || debug);
218 if !should_continue {
219 break;
220 }
221 tracing::info!("Trying to kill vcpu thread...");
222 succeeded |= self.inner.common_kill();
223 std::thread::sleep(self.retry_delay);
224 }
225 succeeded
226 }
227}
228
229#[cfg(any(kvm, mshv3))]
230#[derive(Debug)]
231pub(super) struct LinuxInterruptHandleState {
232 state: InterruptHandleStateMachine,
233
234 tid: AtomicU64,
239
240 dropped: AtomicBool,
242
243 sig_rt_min_offset: u8,
245}
246#[cfg(any(kvm, mshv3))]
247pub(super) type LinuxInterruptHandle = RetryingInterruptHandle<LinuxInterruptHandleState>;
248
249#[cfg(any(kvm, mshv3))]
250impl LinuxInterruptHandle {
251 fn new(config: &crate::sandbox::SandboxConfiguration) -> Self {
252 RetryingInterruptHandle {
253 retry_delay: config.get_interrupt_retry_delay(),
254 inner: LinuxInterruptHandleState {
255 state: InterruptHandleStateMachine::new(),
256 tid: AtomicU64::new(unsafe { libc::pthread_self() as u64 }),
257 sig_rt_min_offset: config.get_interrupt_vcpu_sigrtmin_offset(),
258 dropped: AtomicBool::new(false),
259 },
260 }
261 }
262}
263
264#[cfg(any(kvm, mshv3))]
265impl InterruptHandleImpl for LinuxInterruptHandleState {
266 fn set_tid(&self) {
267 self.tid
271 .store(unsafe { libc::pthread_self() as u64 }, Ordering::Release);
272 }
273
274 fn set_dropped(&self) {
275 self.dropped.store(true, Ordering::Release);
278 }
279}
280
281#[cfg(any(kvm, mshv3))]
282impl InterruptHandle for LinuxInterruptHandleState {
283 fn dropped(&self) -> bool {
284 self.dropped.load(Ordering::Acquire)
287 }
288}
289
290#[cfg(any(kvm, mshv3))]
291impl InterruptHandleInternal for LinuxInterruptHandleState {
292 fn state(&self) -> &InterruptHandleStateMachine {
293 &self.state
294 }
295 fn common_kill(&self) -> bool {
296 let signal_number = libc::SIGRTMIN() + self.sig_rt_min_offset as libc::c_int;
297 unsafe {
298 libc::pthread_kill(self.tid.load(Ordering::Acquire) as _, signal_number);
299 }
300 true
301 }
302}
303
304#[cfg(any(target_os = "windows", hvf))]
305#[derive(Debug)]
306#[allow(private_bounds)]
309pub(super) struct SynchronousInterruptHandle<T: SynchronousInterruptState> {
310 state: InterruptHandleStateMachine,
311 dropped_state: std::sync::RwLock<(bool, T)>,
328}
329#[cfg(any(target_os = "windows", hvf))]
330trait SynchronousInterruptState: Debug + Send + Sync {
331 fn actually_cancel(&self) -> bool;
334
335 #[cfg(hvf)]
336 fn set_vcpu(&mut self, vcpu: Option<hv_vcpu_t>);
337}
338
339#[cfg(any(target_os = "windows", hvf))]
340impl<T: SynchronousInterruptState> InterruptHandleImpl for SynchronousInterruptHandle<T> {
341 #[cfg(hvf)]
342 fn set_vcpu(&self, vcpu: Option<hv_vcpu_t>) {
343 let Ok(mut guard) = self.dropped_state.write() else {
344 return;
345 };
346 guard.1.set_vcpu(vcpu);
347 }
348
349 fn set_dropped(&self) {
350 match self.dropped_state.write() {
357 Ok(mut guard) => {
358 guard.0 = true;
359 }
360 Err(e) => {
361 tracing::error!("Failed to acquire partition_state write lock: {}", e);
362 }
363 }
364 }
365}
366
367#[cfg(any(target_os = "windows", hvf))]
368impl<T: SynchronousInterruptState> InterruptHandle for SynchronousInterruptHandle<T> {
369 fn dropped(&self) -> bool {
370 match self.dropped_state.read() {
372 Ok(guard) => guard.0,
373 Err(e) => {
374 tracing::error!("Failed to acquire partition_state read lock: {}", e);
375 true }
377 }
378 }
379}
380#[cfg(any(target_os = "windows", hvf))]
381impl<T: SynchronousInterruptState> InterruptHandleInternal for SynchronousInterruptHandle<T> {
382 fn state(&self) -> &InterruptHandleStateMachine {
383 &self.state
384 }
385 fn common_kill(&self) -> bool {
386 if !self.state.get_running_cancel_debug().0 {
387 return false;
388 }
389
390 let guard = match self.dropped_state.read() {
394 Ok(guard) => guard,
395 Err(e) => {
396 tracing::error!("Failed to acquire partition_state read lock: {}", e);
397 return false;
398 }
399 };
400
401 if guard.0 {
402 return false;
403 }
404
405 guard.1.actually_cancel()
406 }
407}
408
409#[cfg(target_os = "windows")]
410use windows::Win32::System::Hypervisor::WHV_PARTITION_HANDLE;
411#[cfg(target_os = "windows")]
412pub(super) type WindowsInterruptHandle = SynchronousInterruptHandle<WHV_PARTITION_HANDLE>;
413#[cfg(target_os = "windows")]
414impl WindowsInterruptHandle {
415 fn new(hdl: WHV_PARTITION_HANDLE) -> Self {
416 SynchronousInterruptHandle {
417 state: InterruptHandleStateMachine::new(),
418 dropped_state: std::sync::RwLock::new((false, hdl)),
419 }
420 }
421}
422#[cfg(target_os = "windows")]
423impl SynchronousInterruptState for WHV_PARTITION_HANDLE {
424 fn actually_cancel(&self) -> bool {
425 use windows::Win32::System::Hypervisor::WHvCancelRunVirtualProcessor;
426 unsafe { WHvCancelRunVirtualProcessor(*self, 0, 0).is_ok() }
427 }
428}
429
430#[cfg(hvf)]
431use crate::hypervisor::virtual_machine::hvf::bindings::hv_vcpu_t;
432#[cfg(hvf)]
433pub(super) type HvfInterruptHandle =
434 RetryingInterruptHandle<SynchronousInterruptHandle<Option<hv_vcpu_t>>>;
435#[cfg(hvf)]
436impl SynchronousInterruptState for Option<hv_vcpu_t> {
437 fn actually_cancel(&self) -> bool {
438 use crate::hypervisor::virtual_machine::hvf::bindings::{HV_SUCCESS, hv_vcpus_exit};
439 let Some(vcpu) = self else {
440 return false;
441 };
442 unsafe {
443 hv_vcpus_exit(&raw const *vcpu as *mut hv_vcpu_t, 1).0.0.0 == HV_SUCCESS
446 }
447 }
448
449 fn set_vcpu(&mut self, vcpu: Option<hv_vcpu_t>) {
450 *self = vcpu;
451 }
452}
453#[cfg(hvf)]
454impl HvfInterruptHandle {
455 pub(super) fn new(retry_delay: Duration) -> Self {
456 RetryingInterruptHandle {
457 retry_delay,
458 inner: SynchronousInterruptHandle {
459 state: InterruptHandleStateMachine::new(),
460 dropped_state: std::sync::RwLock::new((false, None)),
461 },
462 }
463 }
464}
465
466#[cfg(all(test, any(target_os = "windows", kvm)))]
467pub(crate) mod tests {
468 use std::sync::{Arc, Mutex};
469
470 use hyperlight_testing::dummy_guest_as_pathbuf;
471
472 use crate::sandbox::uninitialized::GuestBinary;
473 #[cfg(any(crashdump, gdb))]
474 use crate::sandbox::uninitialized::SandboxRuntimeConfig;
475 use crate::sandbox::uninitialized_evolve::set_up_hypervisor_partition;
476 use crate::sandbox::{SandboxConfiguration, UninitializedSandbox};
477 use crate::{Result, is_hypervisor_present};
478
479 #[cfg_attr(feature = "hw-interrupts", ignore)]
480 #[test]
481 fn test_initialise() -> Result<()> {
482 if !is_hypervisor_present() {
483 return Ok(());
484 }
485
486 use crate::mem::ptr::RawPtr;
487 use crate::sandbox::host_funcs::FunctionRegistry;
488
489 let filename = dummy_guest_as_pathbuf();
490
491 let config: SandboxConfiguration = Default::default();
492 #[cfg(any(crashdump, gdb))]
493 let rt_cfg: SandboxRuntimeConfig = Default::default();
494 let sandbox =
495 UninitializedSandbox::new(GuestBinary::FilePath(filename.clone()), Some(config))?;
496 let (mut mem_mgr, gshm) = sandbox.mgr.build().unwrap();
497 let exn_stack_top_gva = hyperlight_common::layout::SCRATCH_TOP_GVA as u64
498 - hyperlight_common::layout::SCRATCH_TOP_EXN_STACK_OFFSET
499 + 1;
500 let mut vm = set_up_hypervisor_partition(
501 gshm,
502 &config,
503 exn_stack_top_gva,
504 page_size::get(),
505 #[cfg(any(crashdump, gdb))]
506 rt_cfg,
507 sandbox.load_info,
508 )?;
509
510 let peb_addr = RawPtr::from(0x1000u64); let seed = 12345u64; let host_funcs = Arc::new(Mutex::new(FunctionRegistry::default()));
514 let guest_max_log_level = Some(tracing_core::LevelFilter::ERROR);
515
516 vm.initialise(
518 peb_addr,
519 seed,
520 &mut mem_mgr,
521 &host_funcs,
522 guest_max_log_level,
523 )
524 .unwrap();
525
526 Ok(())
527 }
528}