hyperlight_host/hypervisor/
hyperv_linux.rs

1/*
2Copyright 2025  The Hyperlight Authors.
3
4Licensed under the Apache License, Version 2.0 (the "License");
5you may not use this file except in compliance with the License.
6You may obtain a copy of the License at
7
8    http://www.apache.org/licenses/LICENSE-2.0
9
10Unless required by applicable law or agreed to in writing, software
11distributed under the License is distributed on an "AS IS" BASIS,
12WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13See the License for the specific language governing permissions and
14limitations under the License.
15*/
16
17#[cfg(mshv2)]
18extern crate mshv_bindings2 as mshv_bindings;
19#[cfg(mshv2)]
20extern crate mshv_ioctls2 as mshv_ioctls;
21
22#[cfg(mshv3)]
23extern crate mshv_bindings3 as mshv_bindings;
24#[cfg(mshv3)]
25extern crate mshv_ioctls3 as mshv_ioctls;
26
27use std::fmt::{Debug, Formatter};
28use std::sync::atomic::{AtomicBool, AtomicU64, Ordering};
29use std::sync::{Arc, Mutex};
30
31use log::{LevelFilter, error};
32#[cfg(mshv2)]
33use mshv_bindings::hv_message;
34use mshv_bindings::{
35    FloatingPointUnit, SegmentRegister, SpecialRegisters, StandardRegisters, hv_message_type,
36    hv_message_type_HVMSG_GPA_INTERCEPT, hv_message_type_HVMSG_UNMAPPED_GPA,
37    hv_message_type_HVMSG_X64_HALT, hv_message_type_HVMSG_X64_IO_PORT_INTERCEPT, hv_register_assoc,
38    hv_register_name_HV_X64_REGISTER_RIP, hv_register_value, mshv_user_mem_region,
39};
40#[cfg(gdb)]
41use mshv_bindings::{
42    HV_INTERCEPT_ACCESS_MASK_EXECUTE, hv_intercept_parameters,
43    hv_intercept_type_HV_INTERCEPT_TYPE_EXCEPTION, hv_message_type_HVMSG_X64_EXCEPTION_INTERCEPT,
44    mshv_install_intercept,
45};
46#[cfg(mshv3)]
47use mshv_bindings::{
48    hv_partition_property_code_HV_PARTITION_PROPERTY_SYNTHETIC_PROC_FEATURES,
49    hv_partition_synthetic_processor_features,
50};
51#[cfg(feature = "trace_guest")]
52use mshv_bindings::{
53    hv_register_name, hv_register_name_HV_X64_REGISTER_RAX, hv_register_name_HV_X64_REGISTER_RBP,
54    hv_register_name_HV_X64_REGISTER_RCX, hv_register_name_HV_X64_REGISTER_RSP,
55};
56use mshv_ioctls::{Mshv, VcpuFd, VmFd};
57use tracing::{Span, instrument};
58#[cfg(crashdump)]
59use {super::crashdump, std::path::Path};
60
61#[cfg(feature = "trace_guest")]
62use super::TraceRegister;
63use super::fpu::{FP_CONTROL_WORD_DEFAULT, FP_TAG_WORD_DEFAULT, MXCSR_DEFAULT};
64#[cfg(gdb)]
65use super::gdb::{
66    DebugCommChannel, DebugMsg, DebugResponse, GuestDebug, MshvDebug, VcpuStopReason,
67};
68#[cfg(gdb)]
69use super::handlers::DbgMemAccessHandlerWrapper;
70#[cfg(feature = "init-paging")]
71use super::{
72    CR0_AM, CR0_ET, CR0_MP, CR0_NE, CR0_PE, CR0_PG, CR0_WP, CR4_OSFXSR, CR4_OSXMMEXCPT, CR4_PAE,
73    EFER_LMA, EFER_LME, EFER_NX, EFER_SCE,
74};
75use super::{HyperlightExit, Hypervisor, InterruptHandle, LinuxInterruptHandle, VirtualCPU};
76#[cfg(gdb)]
77use crate::HyperlightError;
78use crate::hypervisor::get_memory_access_violation;
79use crate::mem::memory_region::{MemoryRegion, MemoryRegionFlags};
80use crate::mem::ptr::{GuestPtr, RawPtr};
81use crate::mem::shared_mem::HostSharedMemory;
82use crate::sandbox::SandboxConfiguration;
83#[cfg(feature = "trace_guest")]
84use crate::sandbox::TraceInfo;
85use crate::sandbox::host_funcs::FunctionRegistry;
86use crate::sandbox::mem_mgr::MemMgrWrapper;
87use crate::sandbox::outb::handle_outb;
88#[cfg(crashdump)]
89use crate::sandbox::uninitialized::SandboxRuntimeConfig;
90use crate::{Result, log_then_return, new_error};
91
92#[cfg(gdb)]
93mod debug {
94    use std::sync::{Arc, Mutex};
95
96    use super::mshv_bindings::hv_x64_exception_intercept_message;
97    use super::{HypervLinuxDriver, *};
98    use crate::hypervisor::gdb::{DebugMsg, DebugResponse, VcpuStopReason, X86_64Regs};
99    use crate::hypervisor::handlers::DbgMemAccessHandlerCaller;
100    use crate::{Result, new_error};
101
102    impl HypervLinuxDriver {
103        /// Resets the debug information to disable debugging
104        fn disable_debug(&mut self) -> Result<()> {
105            let mut debug = MshvDebug::default();
106
107            debug.set_single_step(&self.vcpu_fd, false)?;
108
109            self.debug = Some(debug);
110
111            Ok(())
112        }
113
114        /// Get the reason the vCPU has stopped
115        pub(crate) fn get_stop_reason(
116            &mut self,
117            ex_info: hv_x64_exception_intercept_message,
118        ) -> Result<VcpuStopReason> {
119            let debug = self
120                .debug
121                .as_mut()
122                .ok_or_else(|| new_error!("Debug is not enabled"))?;
123
124            debug.get_stop_reason(&self.vcpu_fd, ex_info.exception_vector, self.entrypoint)
125        }
126
127        pub(crate) fn process_dbg_request(
128            &mut self,
129            req: DebugMsg,
130            dbg_mem_access_fn: Arc<Mutex<dyn DbgMemAccessHandlerCaller>>,
131        ) -> Result<DebugResponse> {
132            if let Some(debug) = self.debug.as_mut() {
133                match req {
134                    DebugMsg::AddHwBreakpoint(addr) => Ok(DebugResponse::AddHwBreakpoint(
135                        debug
136                            .add_hw_breakpoint(&self.vcpu_fd, addr)
137                            .map_err(|e| {
138                                log::error!("Failed to add hw breakpoint: {:?}", e);
139
140                                e
141                            })
142                            .is_ok(),
143                    )),
144                    DebugMsg::AddSwBreakpoint(addr) => Ok(DebugResponse::AddSwBreakpoint(
145                        debug
146                            .add_sw_breakpoint(&self.vcpu_fd, addr, dbg_mem_access_fn)
147                            .map_err(|e| {
148                                log::error!("Failed to add sw breakpoint: {:?}", e);
149
150                                e
151                            })
152                            .is_ok(),
153                    )),
154                    DebugMsg::Continue => {
155                        debug.set_single_step(&self.vcpu_fd, false).map_err(|e| {
156                            log::error!("Failed to continue execution: {:?}", e);
157
158                            e
159                        })?;
160
161                        Ok(DebugResponse::Continue)
162                    }
163                    DebugMsg::DisableDebug => {
164                        self.disable_debug().map_err(|e| {
165                            log::error!("Failed to disable debugging: {:?}", e);
166
167                            e
168                        })?;
169
170                        Ok(DebugResponse::DisableDebug)
171                    }
172                    DebugMsg::GetCodeSectionOffset => {
173                        let offset = dbg_mem_access_fn
174                            .try_lock()
175                            .map_err(|e| {
176                                new_error!("Error locking at {}:{}: {}", file!(), line!(), e)
177                            })?
178                            .get_code_offset()
179                            .map_err(|e| {
180                                log::error!("Failed to get code offset: {:?}", e);
181
182                                e
183                            })?;
184
185                        Ok(DebugResponse::GetCodeSectionOffset(offset as u64))
186                    }
187                    DebugMsg::ReadAddr(addr, len) => {
188                        let mut data = vec![0u8; len];
189
190                        debug
191                            .read_addrs(&self.vcpu_fd, addr, &mut data, dbg_mem_access_fn)
192                            .map_err(|e| {
193                                log::error!("Failed to read from address: {:?}", e);
194
195                                e
196                            })?;
197
198                        Ok(DebugResponse::ReadAddr(data))
199                    }
200                    DebugMsg::ReadRegisters => {
201                        let mut regs = X86_64Regs::default();
202
203                        debug
204                            .read_regs(&self.vcpu_fd, &mut regs)
205                            .map_err(|e| {
206                                log::error!("Failed to read registers: {:?}", e);
207
208                                e
209                            })
210                            .map(|_| DebugResponse::ReadRegisters(regs))
211                    }
212                    DebugMsg::RemoveHwBreakpoint(addr) => Ok(DebugResponse::RemoveHwBreakpoint(
213                        debug
214                            .remove_hw_breakpoint(&self.vcpu_fd, addr)
215                            .map_err(|e| {
216                                log::error!("Failed to remove hw breakpoint: {:?}", e);
217
218                                e
219                            })
220                            .is_ok(),
221                    )),
222                    DebugMsg::RemoveSwBreakpoint(addr) => Ok(DebugResponse::RemoveSwBreakpoint(
223                        debug
224                            .remove_sw_breakpoint(&self.vcpu_fd, addr, dbg_mem_access_fn)
225                            .map_err(|e| {
226                                log::error!("Failed to remove sw breakpoint: {:?}", e);
227
228                                e
229                            })
230                            .is_ok(),
231                    )),
232                    DebugMsg::Step => {
233                        debug.set_single_step(&self.vcpu_fd, true).map_err(|e| {
234                            log::error!("Failed to enable step instruction: {:?}", e);
235
236                            e
237                        })?;
238
239                        Ok(DebugResponse::Step)
240                    }
241                    DebugMsg::WriteAddr(addr, data) => {
242                        debug
243                            .write_addrs(&self.vcpu_fd, addr, &data, dbg_mem_access_fn)
244                            .map_err(|e| {
245                                log::error!("Failed to write to address: {:?}", e);
246
247                                e
248                            })?;
249
250                        Ok(DebugResponse::WriteAddr)
251                    }
252                    DebugMsg::WriteRegisters(regs) => debug
253                        .write_regs(&self.vcpu_fd, &regs)
254                        .map_err(|e| {
255                            log::error!("Failed to write registers: {:?}", e);
256
257                            e
258                        })
259                        .map(|_| DebugResponse::WriteRegisters),
260                }
261            } else {
262                Err(new_error!("Debugging is not enabled"))
263            }
264        }
265
266        pub(crate) fn recv_dbg_msg(&mut self) -> Result<DebugMsg> {
267            let gdb_conn = self
268                .gdb_conn
269                .as_mut()
270                .ok_or_else(|| new_error!("Debug is not enabled"))?;
271
272            gdb_conn.recv().map_err(|e| {
273                new_error!(
274                    "Got an error while waiting to receive a
275                    message: {:?}",
276                    e
277                )
278            })
279        }
280
281        pub(crate) fn send_dbg_msg(&mut self, cmd: DebugResponse) -> Result<()> {
282            log::debug!("Sending {:?}", cmd);
283
284            let gdb_conn = self
285                .gdb_conn
286                .as_mut()
287                .ok_or_else(|| new_error!("Debug is not enabled"))?;
288
289            gdb_conn
290                .send(cmd)
291                .map_err(|e| new_error!("Got an error while sending a response message {:?}", e))
292        }
293    }
294}
295
296/// Determine whether the HyperV for Linux hypervisor API is present
297/// and functional.
298#[instrument(skip_all, parent = Span::current(), level = "Trace")]
299pub(crate) fn is_hypervisor_present() -> bool {
300    match Mshv::new() {
301        Ok(_) => true,
302        Err(_) => {
303            log::info!("MSHV is not available on this system");
304            false
305        }
306    }
307}
308
309/// A Hypervisor driver for HyperV-on-Linux. This hypervisor is often
310/// called the Microsoft Hypervisor (MSHV)
311pub(crate) struct HypervLinuxDriver {
312    _mshv: Mshv,
313    page_size: usize,
314    vm_fd: VmFd,
315    vcpu_fd: VcpuFd,
316    orig_rsp: GuestPtr,
317    entrypoint: u64,
318    interrupt_handle: Arc<LinuxInterruptHandle>,
319    mem_mgr: Option<MemMgrWrapper<HostSharedMemory>>,
320    host_funcs: Option<Arc<Mutex<FunctionRegistry>>>,
321
322    sandbox_regions: Vec<MemoryRegion>, // Initially mapped regions when sandbox is created
323    mmap_regions: Vec<MemoryRegion>,    // Later mapped regions
324
325    #[cfg(gdb)]
326    debug: Option<MshvDebug>,
327    #[cfg(gdb)]
328    gdb_conn: Option<DebugCommChannel<DebugResponse, DebugMsg>>,
329    #[cfg(crashdump)]
330    rt_cfg: SandboxRuntimeConfig,
331    #[cfg(feature = "trace_guest")]
332    #[allow(dead_code)]
333    trace_info: TraceInfo,
334}
335
336impl HypervLinuxDriver {
337    /// Create a new `HypervLinuxDriver`, complete with all registers
338    /// set up to execute a Hyperlight binary inside a HyperV-powered
339    /// sandbox on Linux.
340    ///
341    /// While registers are set up, they will not have been applied to
342    /// the underlying virtual CPU after this function returns. Call the
343    /// `apply_registers` method to do that, or more likely call
344    /// `initialise` to do it for you.
345    #[allow(clippy::too_many_arguments)]
346    // TODO: refactor this function to take fewer arguments. Add trace_info to rt_cfg
347    #[instrument(skip_all, parent = Span::current(), level = "Trace")]
348    pub(crate) fn new(
349        mem_regions: Vec<MemoryRegion>,
350        entrypoint_ptr: GuestPtr,
351        rsp_ptr: GuestPtr,
352        pml4_ptr: GuestPtr,
353        config: &SandboxConfiguration,
354        #[cfg(gdb)] gdb_conn: Option<DebugCommChannel<DebugResponse, DebugMsg>>,
355        #[cfg(crashdump)] rt_cfg: SandboxRuntimeConfig,
356        #[cfg(feature = "trace_guest")] trace_info: TraceInfo,
357    ) -> Result<Self> {
358        let mshv = Mshv::new()?;
359        let pr = Default::default();
360        #[cfg(mshv2)]
361        let vm_fd = mshv.create_vm_with_config(&pr)?;
362        #[cfg(mshv3)]
363        let vm_fd = {
364            // It's important to avoid create_vm() and explicitly use
365            // create_vm_with_args() with an empty arguments structure
366            // here, because otherwise the partition is set up with a SynIC.
367
368            let vm_fd = mshv.create_vm_with_args(&pr)?;
369            let features: hv_partition_synthetic_processor_features = Default::default();
370            vm_fd.hvcall_set_partition_property(
371                hv_partition_property_code_HV_PARTITION_PROPERTY_SYNTHETIC_PROC_FEATURES,
372                unsafe { features.as_uint64[0] },
373            )?;
374            vm_fd.initialize()?;
375            vm_fd
376        };
377
378        let mut vcpu_fd = vm_fd.create_vcpu(0)?;
379
380        #[cfg(gdb)]
381        let (debug, gdb_conn) = if let Some(gdb_conn) = gdb_conn {
382            let mut debug = MshvDebug::new();
383            debug.add_hw_breakpoint(&vcpu_fd, entrypoint_ptr.absolute()?)?;
384
385            // The bellow intercepts make the vCPU exit with the Exception Intercept exit code
386            // Check Table 6-1. Exceptions and Interrupts at Page 6-13 Vol. 1
387            // of Intel 64 and IA-32 Architectures Software Developer's Manual
388            // Install intercept for #DB (1) exception
389            vm_fd
390                .install_intercept(mshv_install_intercept {
391                    access_type_mask: HV_INTERCEPT_ACCESS_MASK_EXECUTE,
392                    intercept_type: hv_intercept_type_HV_INTERCEPT_TYPE_EXCEPTION,
393                    // Exception handler #DB (1)
394                    intercept_parameter: hv_intercept_parameters {
395                        exception_vector: 0x1,
396                    },
397                })
398                .map_err(|e| new_error!("Cannot install debug exception intercept: {}", e))?;
399
400            // Install intercept for #BP (3) exception
401            vm_fd
402                .install_intercept(mshv_install_intercept {
403                    access_type_mask: HV_INTERCEPT_ACCESS_MASK_EXECUTE,
404                    intercept_type: hv_intercept_type_HV_INTERCEPT_TYPE_EXCEPTION,
405                    // Exception handler #BP (3)
406                    intercept_parameter: hv_intercept_parameters {
407                        exception_vector: 0x3,
408                    },
409                })
410                .map_err(|e| new_error!("Cannot install breakpoint exception intercept: {}", e))?;
411
412            (Some(debug), Some(gdb_conn))
413        } else {
414            (None, None)
415        };
416
417        mem_regions.iter().try_for_each(|region| {
418            let mshv_region = region.to_owned().into();
419            vm_fd.map_user_memory(mshv_region)
420        })?;
421
422        Self::setup_initial_sregs(&mut vcpu_fd, pml4_ptr.absolute()?)?;
423
424        let interrupt_handle = Arc::new(LinuxInterruptHandle {
425            running: AtomicU64::new(0),
426            cancel_requested: AtomicBool::new(false),
427            #[cfg(gdb)]
428            debug_interrupt: AtomicBool::new(false),
429            #[cfg(all(
430                target_arch = "x86_64",
431                target_vendor = "unknown",
432                target_os = "linux",
433                target_env = "musl"
434            ))]
435            tid: AtomicU64::new(unsafe { libc::pthread_self() as u64 }),
436            #[cfg(not(all(
437                target_arch = "x86_64",
438                target_vendor = "unknown",
439                target_os = "linux",
440                target_env = "musl"
441            )))]
442            tid: AtomicU64::new(unsafe { libc::pthread_self() }),
443            retry_delay: config.get_interrupt_retry_delay(),
444            sig_rt_min_offset: config.get_interrupt_vcpu_sigrtmin_offset(),
445            dropped: AtomicBool::new(false),
446        });
447
448        #[allow(unused_mut)]
449        let mut hv = Self {
450            _mshv: mshv,
451            page_size: 0,
452            vm_fd,
453            vcpu_fd,
454            sandbox_regions: mem_regions,
455            mmap_regions: Vec::new(),
456            entrypoint: entrypoint_ptr.absolute()?,
457            orig_rsp: rsp_ptr,
458            interrupt_handle: interrupt_handle.clone(),
459            mem_mgr: None,
460            host_funcs: None,
461            #[cfg(gdb)]
462            debug,
463            #[cfg(gdb)]
464            gdb_conn,
465            #[cfg(crashdump)]
466            rt_cfg,
467            #[cfg(feature = "trace_guest")]
468            trace_info,
469        };
470
471        // Send the interrupt handle to the GDB thread if debugging is enabled
472        // This is used to allow the GDB thread to stop the vCPU
473        #[cfg(gdb)]
474        if hv.debug.is_some() {
475            hv.send_dbg_msg(DebugResponse::InterruptHandle(interrupt_handle))?;
476        }
477
478        Ok(hv)
479    }
480
481    #[instrument(err(Debug), skip_all, parent = Span::current(), level = "Trace")]
482    fn setup_initial_sregs(vcpu: &mut VcpuFd, _pml4_addr: u64) -> Result<()> {
483        #[cfg(feature = "init-paging")]
484        let sregs = SpecialRegisters {
485            cr0: CR0_PE | CR0_MP | CR0_ET | CR0_NE | CR0_AM | CR0_PG | CR0_WP,
486            cr4: CR4_PAE | CR4_OSFXSR | CR4_OSXMMEXCPT,
487            cr3: _pml4_addr,
488            efer: EFER_LME | EFER_LMA | EFER_SCE | EFER_NX,
489            cs: SegmentRegister {
490                type_: 11,
491                present: 1,
492                s: 1,
493                l: 1,
494                ..Default::default()
495            },
496            tr: SegmentRegister {
497                limit: 65535,
498                type_: 11,
499                present: 1,
500                ..Default::default()
501            },
502            ..Default::default()
503        };
504
505        #[cfg(not(feature = "init-paging"))]
506        let sregs = SpecialRegisters {
507            cs: SegmentRegister {
508                base: 0,
509                selector: 0,
510                limit: 0xFFFF,
511                type_: 11,
512                present: 1,
513                s: 1,
514                ..Default::default()
515            },
516            ds: SegmentRegister {
517                base: 0,
518                selector: 0,
519                limit: 0xFFFF,
520                type_: 3,
521                present: 1,
522                s: 1,
523                ..Default::default()
524            },
525            tr: SegmentRegister {
526                base: 0,
527                selector: 0,
528                limit: 0xFFFF,
529                type_: 11,
530                present: 1,
531                s: 0,
532                ..Default::default()
533            },
534            ..Default::default()
535        };
536        vcpu.set_sregs(&sregs)?;
537        Ok(())
538    }
539}
540
541impl Debug for HypervLinuxDriver {
542    fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
543        let mut f = f.debug_struct("Hyperv Linux Driver");
544
545        f.field("Entrypoint", &self.entrypoint)
546            .field("Original RSP", &self.orig_rsp);
547
548        for region in &self.sandbox_regions {
549            f.field("Sandbox Memory Region", &region);
550        }
551        for region in &self.mmap_regions {
552            f.field("Mapped Memory Region", &region);
553        }
554
555        let regs = self.vcpu_fd.get_regs();
556
557        if let Ok(regs) = regs {
558            f.field("Registers", &regs);
559        }
560
561        let sregs = self.vcpu_fd.get_sregs();
562
563        if let Ok(sregs) = sregs {
564            f.field("Special Registers", &sregs);
565        }
566
567        f.finish()
568    }
569}
570
571#[cfg(feature = "trace_guest")]
572impl From<TraceRegister> for hv_register_name {
573    fn from(r: TraceRegister) -> Self {
574        match r {
575            TraceRegister::RAX => hv_register_name_HV_X64_REGISTER_RAX,
576            TraceRegister::RCX => hv_register_name_HV_X64_REGISTER_RCX,
577            TraceRegister::RIP => hv_register_name_HV_X64_REGISTER_RIP,
578            TraceRegister::RSP => hv_register_name_HV_X64_REGISTER_RSP,
579            TraceRegister::RBP => hv_register_name_HV_X64_REGISTER_RBP,
580        }
581    }
582}
583
584impl Hypervisor for HypervLinuxDriver {
585    #[instrument(err(Debug), skip_all, parent = Span::current(), level = "Trace")]
586    fn initialise(
587        &mut self,
588        peb_addr: RawPtr,
589        seed: u64,
590        page_size: u32,
591        mem_mgr: MemMgrWrapper<HostSharedMemory>,
592        host_funcs: Arc<Mutex<FunctionRegistry>>,
593        max_guest_log_level: Option<LevelFilter>,
594        #[cfg(gdb)] dbg_mem_access_fn: DbgMemAccessHandlerWrapper,
595    ) -> Result<()> {
596        self.mem_mgr = Some(mem_mgr);
597        self.host_funcs = Some(host_funcs);
598        self.page_size = page_size as usize;
599
600        let max_guest_log_level: u64 = match max_guest_log_level {
601            Some(level) => level as u64,
602            None => self.get_max_log_level().into(),
603        };
604
605        let regs = StandardRegisters {
606            rip: self.entrypoint,
607            rsp: self.orig_rsp.absolute()?,
608            rflags: 2, //bit 1 of rlags is required to be set
609
610            // function args
611            rdi: peb_addr.into(),
612            rsi: seed,
613            rdx: page_size.into(),
614            rcx: max_guest_log_level,
615
616            ..Default::default()
617        };
618        self.vcpu_fd.set_regs(&regs)?;
619
620        VirtualCPU::run(
621            self.as_mut_hypervisor(),
622            #[cfg(gdb)]
623            dbg_mem_access_fn,
624        )
625    }
626
627    #[instrument(err(Debug), skip_all, parent = Span::current(), level = "Trace")]
628    unsafe fn map_region(&mut self, rgn: &MemoryRegion) -> Result<()> {
629        if [
630            rgn.guest_region.start,
631            rgn.guest_region.end,
632            rgn.host_region.start,
633            rgn.host_region.end,
634        ]
635        .iter()
636        .any(|x| x % self.page_size != 0)
637        {
638            log_then_return!("region is not page-aligned");
639        }
640        let mshv_region: mshv_user_mem_region = rgn.to_owned().into();
641        self.vm_fd.map_user_memory(mshv_region)?;
642        self.mmap_regions.push(rgn.to_owned());
643        Ok(())
644    }
645
646    #[instrument(err(Debug), skip_all, parent = Span::current(), level = "Trace")]
647    unsafe fn unmap_region(&mut self, region: &MemoryRegion) -> Result<()> {
648        if let Some(pos) = self.mmap_regions.iter().position(|r| r == region) {
649            let removed_region = self.mmap_regions.remove(pos);
650            let mshv_region: mshv_user_mem_region = removed_region.into();
651            self.vm_fd.unmap_user_memory(mshv_region)?;
652            Ok(())
653        } else {
654            Err(new_error!("Tried to unmap region that is not mapped"))
655        }
656    }
657
658    fn get_mapped_regions(&self) -> Box<dyn ExactSizeIterator<Item = &MemoryRegion> + '_> {
659        Box::new(self.mmap_regions.iter())
660    }
661
662    #[instrument(err(Debug), skip_all, parent = Span::current(), level = "Trace")]
663    fn dispatch_call_from_host(
664        &mut self,
665        dispatch_func_addr: RawPtr,
666        #[cfg(gdb)] dbg_mem_access_fn: DbgMemAccessHandlerWrapper,
667    ) -> Result<()> {
668        // Reset general purpose registers, then set RIP and RSP
669        let regs = StandardRegisters {
670            rip: dispatch_func_addr.into(),
671            rsp: self.orig_rsp.absolute()?,
672            rflags: 2, //bit 1 of rlags is required to be set
673            ..Default::default()
674        };
675        self.vcpu_fd.set_regs(&regs)?;
676
677        // reset fpu state
678        let fpu = FloatingPointUnit {
679            fcw: FP_CONTROL_WORD_DEFAULT,
680            ftwx: FP_TAG_WORD_DEFAULT,
681            mxcsr: MXCSR_DEFAULT,
682            ..Default::default() // zero out the rest
683        };
684        self.vcpu_fd.set_fpu(&fpu)?;
685
686        // run
687        VirtualCPU::run(
688            self.as_mut_hypervisor(),
689            #[cfg(gdb)]
690            dbg_mem_access_fn,
691        )?;
692
693        Ok(())
694    }
695
696    #[instrument(err(Debug), skip_all, parent = Span::current(), level = "Trace")]
697    fn handle_io(
698        &mut self,
699        port: u16,
700        data: Vec<u8>,
701        rip: u64,
702        instruction_length: u64,
703    ) -> Result<()> {
704        let mut padded = [0u8; 4];
705        let copy_len = data.len().min(4);
706        padded[..copy_len].copy_from_slice(&data[..copy_len]);
707        let val = u32::from_le_bytes(padded);
708
709        #[cfg(feature = "trace_guest")]
710        {
711            // We need to handle the borrow checker issue where we need both:
712            // - &mut MemMgrWrapper (from self.mem_mgr.as_mut())
713            // - &mut dyn Hypervisor (from self)
714            // We'll use a temporary approach to extract the mem_mgr temporarily
715            let mem_mgr_option = self.mem_mgr.take();
716            let mut mem_mgr = mem_mgr_option
717                .ok_or_else(|| new_error!("mem_mgr should be initialized before handling IO"))?;
718            let host_funcs = self
719                .host_funcs
720                .as_ref()
721                .ok_or_else(|| new_error!("host_funcs should be initialized before handling IO"))?
722                .clone();
723
724            handle_outb(&mut mem_mgr, host_funcs, self, port, val)?;
725
726            // Put the mem_mgr back
727            self.mem_mgr = Some(mem_mgr);
728        }
729
730        #[cfg(not(feature = "trace_guest"))]
731        {
732            let mem_mgr = self
733                .mem_mgr
734                .as_mut()
735                .ok_or_else(|| new_error!("mem_mgr should be initialized before handling IO"))?;
736            let host_funcs = self
737                .host_funcs
738                .as_ref()
739                .ok_or_else(|| new_error!("host_funcs should be initialized before handling IO"))?
740                .clone();
741
742            handle_outb(mem_mgr, host_funcs, port, val)?;
743        }
744
745        // update rip
746        self.vcpu_fd.set_reg(&[hv_register_assoc {
747            name: hv_register_name_HV_X64_REGISTER_RIP,
748            value: hv_register_value {
749                reg64: rip + instruction_length,
750            },
751            ..Default::default()
752        }])?;
753        Ok(())
754    }
755
756    #[instrument(err(Debug), skip_all, parent = Span::current(), level = "Trace")]
757    fn run(&mut self) -> Result<super::HyperlightExit> {
758        const HALT_MESSAGE: hv_message_type = hv_message_type_HVMSG_X64_HALT;
759        const IO_PORT_INTERCEPT_MESSAGE: hv_message_type =
760            hv_message_type_HVMSG_X64_IO_PORT_INTERCEPT;
761        const UNMAPPED_GPA_MESSAGE: hv_message_type = hv_message_type_HVMSG_UNMAPPED_GPA;
762        const INVALID_GPA_ACCESS_MESSAGE: hv_message_type = hv_message_type_HVMSG_GPA_INTERCEPT;
763        #[cfg(gdb)]
764        const EXCEPTION_INTERCEPT: hv_message_type = hv_message_type_HVMSG_X64_EXCEPTION_INTERCEPT;
765
766        self.interrupt_handle
767            .tid
768            .store(unsafe { libc::pthread_self() as u64 }, Ordering::Relaxed);
769        // Note: if a `InterruptHandle::kill()` called while this thread is **here**
770        // Then this is fine since `cancel_requested` is set to true, so we will skip the `VcpuFd::run()` call
771        self.interrupt_handle
772            .set_running_and_increment_generation()
773            .map_err(|e| {
774                new_error!(
775                    "Error setting running state and incrementing generation: {}",
776                    e
777                )
778            })?;
779        #[cfg(not(gdb))]
780        let debug_interrupt = false;
781        #[cfg(gdb)]
782        let debug_interrupt = self
783            .interrupt_handle
784            .debug_interrupt
785            .load(Ordering::Relaxed);
786
787        // Don't run the vcpu if `cancel_requested` is true
788        //
789        // Note: if a `InterruptHandle::kill()` called while this thread is **here**
790        // Then this is fine since `cancel_requested` is set to true, so we will skip the `VcpuFd::run()` call
791        let exit_reason = if self
792            .interrupt_handle
793            .cancel_requested
794            .load(Ordering::Relaxed)
795            || debug_interrupt
796        {
797            Err(mshv_ioctls::MshvError::from(libc::EINTR))
798        } else {
799            #[cfg(feature = "trace_guest")]
800            if self.trace_info.guest_start_epoch.is_none() {
801                // Store the guest start epoch and cycles to trace the guest execution time
802                crate::debug!("MSHV - Guest Start Epoch set");
803                self.trace_info.guest_start_tsc =
804                    Some(hyperlight_guest_tracing::invariant_tsc::read_tsc());
805                self.trace_info.guest_start_epoch = Some(std::time::Instant::now());
806            }
807            // Note: if a `InterruptHandle::kill()` called while this thread is **here**
808            // Then the vcpu will run, but we will keep sending signals to this thread
809            // to interrupt it until `running` is set to false. The `vcpu_fd::run()` call will
810            // return either normally with an exit reason, or from being "kicked" by out signal handler, with an EINTR error,
811            // both of which are fine.
812            #[cfg(mshv2)]
813            {
814                let hv_message: hv_message = Default::default();
815                self.vcpu_fd.run(hv_message)
816            }
817            #[cfg(mshv3)]
818            self.vcpu_fd.run()
819        };
820        // Note: if a `InterruptHandle::kill()` called while this thread is **here**
821        // Then signals will be sent to this thread until `running` is set to false.
822        // This is fine since the signal handler is a no-op.
823        let cancel_requested = self
824            .interrupt_handle
825            .cancel_requested
826            .load(Ordering::Relaxed);
827        #[cfg(gdb)]
828        let debug_interrupt = self
829            .interrupt_handle
830            .debug_interrupt
831            .load(Ordering::Relaxed);
832        // Note: if a `InterruptHandle::kill()` called while this thread is **here**
833        // Then `cancel_requested` will be set to true again, which will cancel the **next vcpu run**.
834        // Additionally signals will be sent to this thread until `running` is set to false.
835        // This is fine since the signal handler is a no-op.
836        self.interrupt_handle.clear_running_bit();
837        // At this point, `running` is false so no more signals will be sent to this thread,
838        // but we may still receive async signals that were sent before this point.
839        // To prevent those signals from interrupting subsequent calls to `run()`,
840        // we make sure to check `cancel_requested` before cancelling (see `libc::EINTR` match-arm below).
841        let result = match exit_reason {
842            Ok(m) => match m.header.message_type {
843                HALT_MESSAGE => {
844                    crate::debug!("mshv - Halt Details : {:#?}", &self);
845                    HyperlightExit::Halt()
846                }
847                IO_PORT_INTERCEPT_MESSAGE => {
848                    let io_message = m.to_ioport_info().map_err(mshv_ioctls::MshvError::from)?;
849                    let port_number = io_message.port_number;
850                    let rip = io_message.header.rip;
851                    let rax = io_message.rax;
852                    let instruction_length = io_message.header.instruction_length() as u64;
853                    crate::debug!("mshv IO Details : \nPort : {}\n{:#?}", port_number, &self);
854                    HyperlightExit::IoOut(
855                        port_number,
856                        rax.to_le_bytes().to_vec(),
857                        rip,
858                        instruction_length,
859                    )
860                }
861                UNMAPPED_GPA_MESSAGE => {
862                    let mimo_message = m.to_memory_info().map_err(mshv_ioctls::MshvError::from)?;
863                    let addr = mimo_message.guest_physical_address;
864                    crate::debug!(
865                        "mshv MMIO unmapped GPA -Details: Address: {} \n {:#?}",
866                        addr,
867                        &self
868                    );
869                    HyperlightExit::Mmio(addr)
870                }
871                INVALID_GPA_ACCESS_MESSAGE => {
872                    let mimo_message = m.to_memory_info().map_err(mshv_ioctls::MshvError::from)?;
873                    let gpa = mimo_message.guest_physical_address;
874                    let access_info = MemoryRegionFlags::try_from(mimo_message)?;
875                    crate::debug!(
876                        "mshv MMIO invalid GPA access -Details: Address: {} \n {:#?}",
877                        gpa,
878                        &self
879                    );
880                    match get_memory_access_violation(
881                        gpa as usize,
882                        self.sandbox_regions.iter().chain(self.mmap_regions.iter()),
883                        access_info,
884                    ) {
885                        Some(access_info_violation) => access_info_violation,
886                        None => HyperlightExit::Mmio(gpa),
887                    }
888                }
889                // The only case an intercept exit is expected is when debugging is enabled
890                // and the intercepts are installed.
891                // Provide the extra information about the exception to accurately determine
892                // the stop reason
893                #[cfg(gdb)]
894                EXCEPTION_INTERCEPT => {
895                    // Extract exception info from the message so we can figure out
896                    // more information about the vCPU state
897                    let ex_info = match m.to_exception_info().map_err(mshv_ioctls::MshvError::from)
898                    {
899                        Ok(info) => info,
900                        Err(e) => {
901                            log_then_return!("Error converting to exception info: {:?}", e);
902                        }
903                    };
904
905                    match self.get_stop_reason(ex_info) {
906                        Ok(reason) => HyperlightExit::Debug(reason),
907                        Err(e) => {
908                            log_then_return!("Error getting stop reason: {:?}", e);
909                        }
910                    }
911                }
912                other => {
913                    crate::debug!("mshv Other Exit: Exit: {:#?} \n {:#?}", other, &self);
914                    #[cfg(crashdump)]
915                    let _ = crashdump::generate_crashdump(self);
916                    log_then_return!("unknown Hyper-V run message type {:?}", other);
917                }
918            },
919            Err(e) => match e.errno() {
920                // we send a signal to the thread to cancel execution this results in EINTR being returned by KVM so we return Cancelled
921                libc::EINTR => {
922                    // If cancellation was not requested for this specific vm, the vcpu was interrupted because of debug interrupt or
923                    // a stale signal that meant to be delivered to a previous/other vcpu on this same thread, so let's ignore it
924                    if cancel_requested {
925                        self.interrupt_handle
926                            .cancel_requested
927                            .store(false, Ordering::Relaxed);
928                        HyperlightExit::Cancelled()
929                    } else {
930                        #[cfg(gdb)]
931                        if debug_interrupt {
932                            self.interrupt_handle
933                                .debug_interrupt
934                                .store(false, Ordering::Relaxed);
935
936                            // If the vCPU was stopped because of an interrupt, we need to
937                            // return a special exit reason so that the gdb thread can handle it
938                            // and resume execution
939                            HyperlightExit::Debug(VcpuStopReason::Interrupt)
940                        } else {
941                            HyperlightExit::Retry()
942                        }
943
944                        #[cfg(not(gdb))]
945                        HyperlightExit::Retry()
946                    }
947                }
948                libc::EAGAIN => HyperlightExit::Retry(),
949                _ => {
950                    crate::debug!("mshv Error - Details: Error: {} \n {:#?}", e, &self);
951                    log_then_return!("Error running VCPU {:?}", e);
952                }
953            },
954        };
955        Ok(result)
956    }
957
958    #[instrument(skip_all, parent = Span::current(), level = "Trace")]
959    fn as_mut_hypervisor(&mut self) -> &mut dyn Hypervisor {
960        self as &mut dyn Hypervisor
961    }
962
963    fn interrupt_handle(&self) -> Arc<dyn InterruptHandle> {
964        self.interrupt_handle.clone()
965    }
966
967    #[cfg(crashdump)]
968    fn crashdump_context(&self) -> Result<Option<super::crashdump::CrashDumpContext>> {
969        if self.rt_cfg.guest_core_dump {
970            let mut regs = [0; 27];
971
972            let vcpu_regs = self.vcpu_fd.get_regs()?;
973            let sregs = self.vcpu_fd.get_sregs()?;
974            let xsave = self.vcpu_fd.get_xsave()?;
975
976            // Set up the registers for the crash dump
977            regs[0] = vcpu_regs.r15; // r15
978            regs[1] = vcpu_regs.r14; // r14
979            regs[2] = vcpu_regs.r13; // r13
980            regs[3] = vcpu_regs.r12; // r12
981            regs[4] = vcpu_regs.rbp; // rbp
982            regs[5] = vcpu_regs.rbx; // rbx
983            regs[6] = vcpu_regs.r11; // r11
984            regs[7] = vcpu_regs.r10; // r10
985            regs[8] = vcpu_regs.r9; // r9
986            regs[9] = vcpu_regs.r8; // r8
987            regs[10] = vcpu_regs.rax; // rax
988            regs[11] = vcpu_regs.rcx; // rcx
989            regs[12] = vcpu_regs.rdx; // rdx
990            regs[13] = vcpu_regs.rsi; // rsi
991            regs[14] = vcpu_regs.rdi; // rdi
992            regs[15] = 0; // orig rax
993            regs[16] = vcpu_regs.rip; // rip
994            regs[17] = sregs.cs.selector as u64; // cs
995            regs[18] = vcpu_regs.rflags; // eflags
996            regs[19] = vcpu_regs.rsp; // rsp
997            regs[20] = sregs.ss.selector as u64; // ss
998            regs[21] = sregs.fs.base; // fs_base
999            regs[22] = sregs.gs.base; // gs_base
1000            regs[23] = sregs.ds.selector as u64; // ds
1001            regs[24] = sregs.es.selector as u64; // es
1002            regs[25] = sregs.fs.selector as u64; // fs
1003            regs[26] = sregs.gs.selector as u64; // gs
1004
1005            // Get the filename from the binary path
1006            let filename = self.rt_cfg.binary_path.clone().and_then(|path| {
1007                Path::new(&path)
1008                    .file_name()
1009                    .and_then(|name| name.to_os_string().into_string().ok())
1010            });
1011
1012            Ok(Some(crashdump::CrashDumpContext::new(
1013                &self.sandbox_regions,
1014                regs,
1015                xsave.buffer.to_vec(),
1016                self.entrypoint,
1017                self.rt_cfg.binary_path.clone(),
1018                filename,
1019            )))
1020        } else {
1021            Ok(None)
1022        }
1023    }
1024
1025    #[cfg(gdb)]
1026    fn handle_debug(
1027        &mut self,
1028        dbg_mem_access_fn: std::sync::Arc<
1029            std::sync::Mutex<dyn super::handlers::DbgMemAccessHandlerCaller>,
1030        >,
1031        stop_reason: VcpuStopReason,
1032    ) -> Result<()> {
1033        if self.debug.is_none() {
1034            return Err(new_error!("Debugging is not enabled"));
1035        }
1036
1037        match stop_reason {
1038            // If the vCPU stopped because of a crash, we need to handle it differently
1039            // We do not want to allow resuming execution or placing breakpoints
1040            // because the guest has crashed.
1041            // We only allow reading registers and memory
1042            VcpuStopReason::Crash => {
1043                self.send_dbg_msg(DebugResponse::VcpuStopped(stop_reason))
1044                    .map_err(|e| {
1045                        new_error!("Couldn't signal vCPU stopped event to GDB thread: {:?}", e)
1046                    })?;
1047
1048                loop {
1049                    log::debug!("Debug wait for event to resume vCPU");
1050                    // Wait for a message from gdb
1051                    let req = self.recv_dbg_msg()?;
1052
1053                    // Flag to store if we should deny continue or step requests
1054                    let mut deny_continue = false;
1055                    // Flag to store if we should detach from the gdb session
1056                    let mut detach = false;
1057
1058                    let response = match req {
1059                        // Allow the detach request to disable debugging by continuing resuming
1060                        // hypervisor crash error reporting
1061                        DebugMsg::DisableDebug => {
1062                            detach = true;
1063                            DebugResponse::DisableDebug
1064                        }
1065                        // Do not allow continue or step requests
1066                        DebugMsg::Continue | DebugMsg::Step => {
1067                            deny_continue = true;
1068                            DebugResponse::NotAllowed
1069                        }
1070                        // Do not allow adding/removing breakpoints and writing to memory or registers
1071                        DebugMsg::AddHwBreakpoint(_)
1072                        | DebugMsg::AddSwBreakpoint(_)
1073                        | DebugMsg::RemoveHwBreakpoint(_)
1074                        | DebugMsg::RemoveSwBreakpoint(_)
1075                        | DebugMsg::WriteAddr(_, _)
1076                        | DebugMsg::WriteRegisters(_) => DebugResponse::NotAllowed,
1077
1078                        // For all other requests, we will process them normally
1079                        _ => {
1080                            let result = self.process_dbg_request(req, dbg_mem_access_fn.clone());
1081                            match result {
1082                                Ok(response) => response,
1083                                Err(HyperlightError::TranslateGuestAddress(_)) => {
1084                                    // Treat non fatal errors separately so the guest doesn't fail
1085                                    DebugResponse::ErrorOccurred
1086                                }
1087                                Err(e) => {
1088                                    log::error!("Error processing debug request: {:?}", e);
1089                                    return Err(e);
1090                                }
1091                            }
1092                        }
1093                    };
1094
1095                    // Send the response to the request back to gdb
1096                    self.send_dbg_msg(response)
1097                        .map_err(|e| new_error!("Couldn't send response to gdb: {:?}", e))?;
1098
1099                    // If we are denying continue or step requests, the debugger assumes the
1100                    // execution started so we need to report a stop reason as a crash and let
1101                    // it request to read registers/memory to figure out what happened
1102                    if deny_continue {
1103                        self.send_dbg_msg(DebugResponse::VcpuStopped(VcpuStopReason::Crash))
1104                            .map_err(|e| new_error!("Couldn't send response to gdb: {:?}", e))?;
1105                    }
1106
1107                    // If we are detaching, we will break the loop and the Hypervisor will continue
1108                    // to handle the Crash reason
1109                    if detach {
1110                        break;
1111                    }
1112                }
1113            }
1114            // If the vCPU stopped because of any other reason except a crash, we can handle it
1115            // normally
1116            _ => {
1117                // Send the stop reason to the gdb thread
1118                self.send_dbg_msg(DebugResponse::VcpuStopped(stop_reason))
1119                    .map_err(|e| {
1120                        new_error!("Couldn't signal vCPU stopped event to GDB thread: {:?}", e)
1121                    })?;
1122
1123                loop {
1124                    log::debug!("Debug wait for event to resume vCPU");
1125                    // Wait for a message from gdb
1126                    let req = self.recv_dbg_msg()?;
1127
1128                    let result = self.process_dbg_request(req, dbg_mem_access_fn.clone());
1129
1130                    let response = match result {
1131                        Ok(response) => response,
1132                        // Treat non fatal errors separately so the guest doesn't fail
1133                        Err(HyperlightError::TranslateGuestAddress(_)) => {
1134                            DebugResponse::ErrorOccurred
1135                        }
1136                        Err(e) => {
1137                            return Err(e);
1138                        }
1139                    };
1140
1141                    let cont = matches!(
1142                        response,
1143                        DebugResponse::Continue | DebugResponse::Step | DebugResponse::DisableDebug
1144                    );
1145
1146                    self.send_dbg_msg(response)
1147                        .map_err(|e| new_error!("Couldn't send response to gdb: {:?}", e))?;
1148
1149                    // Check if we should continue execution
1150                    // We continue if the response is one of the following: Step, Continue, or DisableDebug
1151                    if cont {
1152                        break;
1153                    }
1154                }
1155            }
1156        }
1157
1158        Ok(())
1159    }
1160
1161    fn check_stack_guard(&self) -> Result<bool> {
1162        if let Some(mgr) = self.mem_mgr.as_ref() {
1163            mgr.check_stack_guard()
1164        } else {
1165            Err(new_error!("Memory manager is not initialized"))
1166        }
1167    }
1168
1169    #[cfg(feature = "trace_guest")]
1170    fn read_trace_reg(&self, reg: TraceRegister) -> Result<u64> {
1171        let mut assoc = [hv_register_assoc {
1172            name: reg.into(),
1173            ..Default::default()
1174        }];
1175        self.vcpu_fd.get_reg(&mut assoc)?;
1176        // safety: all registers that we currently support are 64-bit
1177        unsafe { Ok(assoc[0].value.reg64) }
1178    }
1179
1180    #[cfg(feature = "trace_guest")]
1181    fn trace_info_as_ref(&self) -> &TraceInfo {
1182        &self.trace_info
1183    }
1184    #[cfg(feature = "trace_guest")]
1185    fn trace_info_as_mut(&mut self) -> &mut TraceInfo {
1186        &mut self.trace_info
1187    }
1188}
1189
1190impl Drop for HypervLinuxDriver {
1191    #[instrument(skip_all, parent = Span::current(), level = "Trace")]
1192    fn drop(&mut self) {
1193        self.interrupt_handle.dropped.store(true, Ordering::Relaxed);
1194        for region in self.sandbox_regions.iter().chain(self.mmap_regions.iter()) {
1195            let mshv_region: mshv_user_mem_region = region.to_owned().into();
1196            match self.vm_fd.unmap_user_memory(mshv_region) {
1197                Ok(_) => (),
1198                Err(e) => error!("Failed to unmap user memory in HyperVOnLinux ({:?})", e),
1199            }
1200        }
1201    }
1202}
1203
1204#[cfg(test)]
1205mod tests {
1206    use super::*;
1207    #[cfg(feature = "unwind_guest")]
1208    use crate::mem::exe::DummyUnwindInfo;
1209    use crate::mem::memory_region::MemoryRegionVecBuilder;
1210    use crate::mem::shared_mem::{ExclusiveSharedMemory, SharedMemory};
1211
1212    #[rustfmt::skip]
1213    const CODE: [u8; 12] = [
1214        0xba, 0xf8, 0x03, /* mov $0x3f8, %dx */
1215        0x00, 0xd8, /* add %bl, %al */
1216        0x04, b'0', /* add $'0', %al */
1217        0xee, /* out %al, (%dx) */
1218        /* send a 0 to indicate we're done */
1219        0xb0, b'\0', /* mov $'\0', %al */
1220        0xee, /* out %al, (%dx) */
1221        0xf4, /* HLT */
1222    ];
1223
1224    fn shared_mem_with_code(
1225        code: &[u8],
1226        mem_size: usize,
1227        load_offset: usize,
1228    ) -> Result<Box<ExclusiveSharedMemory>> {
1229        if load_offset > mem_size {
1230            log_then_return!(
1231                "code load offset ({}) > memory size ({})",
1232                load_offset,
1233                mem_size
1234            );
1235        }
1236        let mut shared_mem = ExclusiveSharedMemory::new(mem_size)?;
1237        shared_mem.copy_from_slice(code, load_offset)?;
1238        Ok(Box::new(shared_mem))
1239    }
1240
1241    #[test]
1242    fn create_driver() {
1243        if !super::is_hypervisor_present() {
1244            return;
1245        }
1246        const MEM_SIZE: usize = 0x3000;
1247        let gm = shared_mem_with_code(CODE.as_slice(), MEM_SIZE, 0).unwrap();
1248        let rsp_ptr = GuestPtr::try_from(0).unwrap();
1249        let pml4_ptr = GuestPtr::try_from(0).unwrap();
1250        let entrypoint_ptr = GuestPtr::try_from(0).unwrap();
1251        let mut regions = MemoryRegionVecBuilder::new(0, gm.base_addr());
1252        regions.push_page_aligned(
1253            MEM_SIZE,
1254            MemoryRegionFlags::READ | MemoryRegionFlags::WRITE | MemoryRegionFlags::EXECUTE,
1255            crate::mem::memory_region::MemoryRegionType::Code,
1256        );
1257        let config: SandboxConfiguration = Default::default();
1258
1259        super::HypervLinuxDriver::new(
1260            regions.build(),
1261            entrypoint_ptr,
1262            rsp_ptr,
1263            pml4_ptr,
1264            &config,
1265            #[cfg(gdb)]
1266            None,
1267            #[cfg(crashdump)]
1268            SandboxRuntimeConfig {
1269                #[cfg(crashdump)]
1270                binary_path: None,
1271                #[cfg(gdb)]
1272                debug_info: None,
1273                #[cfg(crashdump)]
1274                guest_core_dump: true,
1275            },
1276            #[cfg(feature = "trace_guest")]
1277            TraceInfo::new(
1278                #[cfg(feature = "unwind_guest")]
1279                Arc::new(DummyUnwindInfo {}),
1280            )
1281            .unwrap(),
1282        )
1283        .unwrap();
1284    }
1285}